Merged Irssi SVN (upcoming irssi 0.8.11).
authorPekka Riikonen <priikone@silcnet.org>
Thu, 25 Jan 2007 12:02:13 +0000 (12:02 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Thu, 25 Jan 2007 12:02:13 +0000 (12:02 +0000)
155 files changed:
CHANGES
apps/irssi/AUTHORS
apps/irssi/NEWS
apps/irssi/TODO
apps/irssi/acconfig.h
apps/irssi/configure.in
apps/irssi/docs/crash.txt
apps/irssi/docs/faq.html [new file with mode: 0644]
apps/irssi/docs/help/Makefile.am.gen
apps/irssi/docs/help/in/recode.in [new file with mode: 0644]
apps/irssi/docs/manual.txt
apps/irssi/docs/perl.txt
apps/irssi/docs/signals.txt
apps/irssi/docs/special_vars.txt
apps/irssi/docs/startup-HOWTO-rus.html [new file with mode: 0644]
apps/irssi/docs/startup-HOWTO.html
apps/irssi/glib-2.0.m4_
apps/irssi/scripts/Makefile.am
apps/irssi/scripts/autoop.pl
apps/irssi/scripts/autorejoin.pl [new file with mode: 0644]
apps/irssi/scripts/buf.pl [new file with mode: 0644]
apps/irssi/scripts/dns.pl
apps/irssi/scripts/examples/Makefile.am [new file with mode: 0644]
apps/irssi/scripts/examples/command.pl [new file with mode: 0644]
apps/irssi/scripts/examples/msg-event.pl [new file with mode: 0644]
apps/irssi/scripts/examples/redirect.pl [new file with mode: 0644]
apps/irssi/scripts/kills.pl [new file with mode: 0644]
apps/irssi/scripts/mail.pl
apps/irssi/scripts/mlock.pl [new file with mode: 0644]
apps/irssi/scripts/quitmsg.pl [new file with mode: 0644]
apps/irssi/scripts/scriptassist.pl [new file with mode: 0644]
apps/irssi/scripts/splitlong.pl [new file with mode: 0644]
apps/irssi/scripts/usercount.pl [new file with mode: 0644]
apps/irssi/src/Makefile.am
apps/irssi/src/common.h
apps/irssi/src/core/Makefile.am
apps/irssi/src/core/channels.c
apps/irssi/src/core/chat-commands.c
apps/irssi/src/core/chatnets.c
apps/irssi/src/core/commands.c
apps/irssi/src/core/commands.h
apps/irssi/src/core/core.c
apps/irssi/src/core/core.h
apps/irssi/src/core/expandos.c
apps/irssi/src/core/line-split.c
apps/irssi/src/core/log-away.c
apps/irssi/src/core/log.c
apps/irssi/src/core/misc.c
apps/irssi/src/core/misc.h
apps/irssi/src/core/modules-load.c
apps/irssi/src/core/modules.c
apps/irssi/src/core/net-nonblock.c
apps/irssi/src/core/net-sendbuffer.c
apps/irssi/src/core/net-sendbuffer.h
apps/irssi/src/core/network-openssl.c
apps/irssi/src/core/network.c
apps/irssi/src/core/network.h
apps/irssi/src/core/nick-rec.h
apps/irssi/src/core/nicklist.c
apps/irssi/src/core/nicklist.h
apps/irssi/src/core/recode.c [new file with mode: 0644]
apps/irssi/src/core/recode.h [new file with mode: 0644]
apps/irssi/src/core/server-connect-rec.h
apps/irssi/src/core/server-rec.h
apps/irssi/src/core/server-setup-rec.h
apps/irssi/src/core/servers-reconnect.c
apps/irssi/src/core/servers-setup.c
apps/irssi/src/core/servers.c
apps/irssi/src/core/session.c
apps/irssi/src/core/session.h
apps/irssi/src/core/settings.c
apps/irssi/src/core/settings.h
apps/irssi/src/core/write-buffer.c
apps/irssi/src/fe-common/core/Makefile.am
apps/irssi/src/fe-common/core/chat-completion.c
apps/irssi/src/fe-common/core/completion.c
apps/irssi/src/fe-common/core/fe-channels.c
apps/irssi/src/fe-common/core/fe-common-core.c
apps/irssi/src/fe-common/core/fe-core-commands.c
apps/irssi/src/fe-common/core/fe-exec.c
apps/irssi/src/fe-common/core/fe-exec.h
apps/irssi/src/fe-common/core/fe-help.c
apps/irssi/src/fe-common/core/fe-ignore.c
apps/irssi/src/fe-common/core/fe-log.c
apps/irssi/src/fe-common/core/fe-messages.c
apps/irssi/src/fe-common/core/fe-modules.c
apps/irssi/src/fe-common/core/fe-queries.c
apps/irssi/src/fe-common/core/fe-recode.c [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-recode.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/fe-server.c
apps/irssi/src/fe-common/core/fe-settings.c
apps/irssi/src/fe-common/core/fe-windows.c
apps/irssi/src/fe-common/core/formats.c
apps/irssi/src/fe-common/core/hilight-text.c
apps/irssi/src/fe-common/core/hilight-text.h
apps/irssi/src/fe-common/core/keyboard.c
apps/irssi/src/fe-common/core/keyboard.h
apps/irssi/src/fe-common/core/module-formats.c
apps/irssi/src/fe-common/core/module-formats.h
apps/irssi/src/fe-common/core/module.h
apps/irssi/src/fe-common/core/printtext.c
apps/irssi/src/fe-common/core/themes.c
apps/irssi/src/fe-common/core/themes.h
apps/irssi/src/fe-common/core/utf8.c [moved from apps/irssi/src/fe-text/utf8.c with 92% similarity]
apps/irssi/src/fe-common/core/utf8.h [new file with mode: 0644]
apps/irssi/src/fe-common/core/window-activity.c
apps/irssi/src/fe-common/core/window-commands.c
apps/irssi/src/fe-common/core/windows-layout.c
apps/irssi/src/fe-text/Makefile.am
apps/irssi/src/fe-text/cuix-api.c [new file with mode: 0644]
apps/irssi/src/fe-text/cuix-api.h [new file with mode: 0644]
apps/irssi/src/fe-text/cuix-lib.c [new file with mode: 0644]
apps/irssi/src/fe-text/cuix-lib.h [new file with mode: 0644]
apps/irssi/src/fe-text/cuix.c [new file with mode: 0644]
apps/irssi/src/fe-text/cuix.h [new file with mode: 0644]
apps/irssi/src/fe-text/gui-entry.c
apps/irssi/src/fe-text/gui-entry.h
apps/irssi/src/fe-text/gui-printtext.c
apps/irssi/src/fe-text/gui-readline.c
apps/irssi/src/fe-text/lastlog.c
apps/irssi/src/fe-text/mainwindows.c
apps/irssi/src/fe-text/module-formats.c
apps/irssi/src/fe-text/module-formats.h
apps/irssi/src/fe-text/silc.c
apps/irssi/src/fe-text/statusbar-items.c
apps/irssi/src/fe-text/statusbar.c
apps/irssi/src/fe-text/term-curses.c
apps/irssi/src/fe-text/term-curses.h [new file with mode: 0644]
apps/irssi/src/fe-text/term-terminfo.c
apps/irssi/src/fe-text/term.c
apps/irssi/src/fe-text/terminfo-core.c
apps/irssi/src/fe-text/textbuffer-commands.c
apps/irssi/src/fe-text/textbuffer-reformat.c
apps/irssi/src/fe-text/textbuffer-view.c
apps/irssi/src/fe-text/textbuffer.c
apps/irssi/src/fe-text/utf8.h [deleted file]
apps/irssi/src/lib-config/parse.c
apps/irssi/src/perl/Makefile.am
apps/irssi/src/perl/common/Core.xs
apps/irssi/src/perl/common/Irssi.xs
apps/irssi/src/perl/common/Server.xs
apps/irssi/src/perl/common/Settings.xs
apps/irssi/src/perl/get-signals.pl
apps/irssi/src/perl/perl-common.c
apps/irssi/src/perl/perl-core.c
apps/irssi/src/perl/perl-fe.c
apps/irssi/src/perl/perl-signals.c
apps/irssi/src/perl/perl-signals.h
apps/irssi/src/perl/textui/TextBuffer.xs
apps/irssi/src/perl/textui/TextUI.xs
apps/irssi/src/perl/ui/Formats.xs
apps/irssi/src/perl/ui/Themes.xs
apps/irssi/src/perl/ui/UI.xs
apps/irssi/src/perl/ui/Window.xs
apps/irssi/src/silc/core/silc-servers.c

diff --git a/CHANGES b/CHANGES
index ef32bfc29fc25499645117182e2da50938855c7b..ce0636bb48f11187f484ed3a1963ff4ac8c697ea 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+Wed Jan 24 18:55:21 EET 2007  Pekka Riikonen <priikone@silcnet.org>
+
+       * Merged Irssi SVN (irssi 0.8.11).  Affected files in apps/irssi/.
+
 Tue Jan 23 16:05:27 EET 2007  Pekka Riikonen <priikone@silcnet.org>
 
        * The silc_schedule_set_listen_fd now returns boolean value.
index 093f5e94c1fe5f5c96393e7b1c5284bbce805600..62388307d8bbcf8341a0ddc4af0c24b3df032f3c 100644 (file)
@@ -1 +1,67 @@
-Timo Sirainen <cras@irssi.org>
+Original code:
+
+  Timo Sirainen <cras@irssi.org>
+
+Irssi staff (current maintainers) <staff@irssi.org>:
+
+  Valentin Batz (senneth, vb)
+  Wouter Coekaerts (coekie)
+  Jochen Eisinger (c0ffee)
+  Geert Hauwaerts
+  Emanuele Giaquinta (exg)
+
+Large feature patches by:
+
+  David Leadbeater (dg, dgl) : isupport
+  vjt@users.sf.net : SSL support
+  Joel Eriksson : SSL certs
+  Heikki Orsila : DCC SEND queueing
+  Mark Trumbull : DCC SERVER
+  Francesco Fracassi : Passive DCC
+
+Other patches (grep for "patch" in ChangeLog) by:
+
+  Toby Peterson
+  Soren Jacobsen
+  Kuang-che Wu
+  Joost Vunderink (Garion)
+  Wang WenRui
+  Jean-Yves Lefort (decadix)
+  Joel Eriksson
+  Maarten van der Zwaart
+  Noah Levitt
+  Krzysztof Kowalik (Borys)
+  Peder Stray
+  mls@suse.de
+  nix@suhs.nu
+  Marcin Kowalczyk (Qrczak)
+  Petr Baudis
+  Bjoern Krombholz (fuchs)
+  aldem-irssi@aldem.net,
+  BC-bd
+  Juerd
+  Han
+  pv2b
+  Tommi Komulainen (tommik)
+  mike@po.cs.msu.su
+  zinx@magenet.net
+  yathen@web.de
+  paul@raade.org
+  Leszek Matok
+  tygrys@moo.pl
+  manoj@io.com
+  cph@cph.demon.co.uk
+  ganesh@earth.li
+  Jakub Jankowski (shasta)
+  vanilla@freebsd.org
+  Tinuk
+  Mark Glines
+  Kjetil Ødegaard
+  Chris Moore
+  ComradeP
+  Jilles Tjoelker
+  Lauri Nurmi
+  Mikko Rauhala
+  loafier
+  Nicolas Collignon
+  Daniel Koning
index ccdc0e4fc80c329c710351ca53fd85d29a9f1a0c..5d94290d75b00b1ce6f42e52a4fc282a641ffb14 100644 (file)
@@ -1,3 +1,238 @@
+v0.8.11 200x-xx-xx  The Irssi team <staff@irssi.org>
+       + Send /QUOTE immediately if server didn't send the 001 event yet
+       + If dcc_own_ip contains IPv4 address, listen only in IPv4
+       + Negative scroll_page_count scrolls screensize-n lines
+         (Patch by Chris Moore)
+       + Sort nicks with custom prefix by the order defined in isupport in /NAMES
+       + New perl command send_raw_first, patch by ComradeP (Bug 413)
+       + Let the module loader also check for fe_common_$protocol files
+       + Don't wait for all /NAMES replies before syncing if we can't combine
+         queries anyways (Patch by jilles)
+       + Renamed irc.efnet.net to irc.efnet.org
+       + Add support for utf8 to Irssi::settings_get_str and
+         Irssi::TextUI::Line::get_text
+       + /SCROLLBACK CLEAR accepts the same arguments as /CLEAR
+       + Check if binary exists and can be executed before /UPGRADE
+       + Change default value of override_coredump_limit to OFF
+       + UPTIME command by Lauri Nurmi with some modifications (Bug 458)
+       + Remove CR and LF from Perl commands, to make it harder to introduce a
+         security bug
+       - Fixed segfault on quit introduced in 0.8.10
+       - Fixed a bug where tab-complete didn't worked with utf8/big5 properly
+       - Ignore joins without a nick from broken servers
+       - Fix whois_hide_safe_channel_id: only look at the beginning of a channel
+         name, not in the middle
+       - Don't assume that 7bit ascii strings are encoded in UTF-8, only
+         validate the strings when they contain octest with highest bit set
+         (patch by Mikko Rauhala)
+       - Make random really random when resolving
+       - Don't get confused by a join command with too many arguments, keys
+         can't have spaces in them (Bug 437)
+       - Don't crash on /QUIT with scripts causing and catching signals on UNLOAD
+       - Fix off-by-one error in gui_entry_draw_from
+       - Fix %k and %K mappings in curses frontend
+       - Fix bold on monochrome terminals in terminfo frontend
+       - Fixed colors with TERM=xterm-{88,256}color in terminfo frontend
+       - Fix building with srcdir != builddir
+       - Don't get confused and keep saying "Netsplit over" on every join for
+         user that only rejoined some channels
+       - Fix crash with one line high terminal
+       - Fix crash in /EXEC (Bug 439)
+       - Fix format string in printtext_dest call from Perl, patch by loafier
+       - Fix memory leaks in expandos_deinit by Nicolas Collignon (Bug 419)
+       - Detect off_t size using AC_CHECK_SIZEOF because it works also when
+         cross-compiling in autoconf-2.50 and higher
+       - Fix failed assertion when the config file is unreadable, patch by
+         Daniel Koning (Bug 164)
+
+v0.8.10 2005-12-11  The Irssi team <staff@irssi.org>
+
+       * Long delayed release, with lots of changes. Most important ones:
+           + Recode support, by decadix and senneth
+           + isupport (005 numeric), by David Leadbeater
+           + Passive DCC support, by Francesco Fracassi
+           - Many memleak fixes, by Toby Peterson
+
+       + Moved to subversion
+       + /SET paste_join_multiline ON - When paste detection is enabled and
+         you paste lines which look like they're being copy&pasted from irssi
+         itself, it attempts to merge lines said originally in a single line.
+
+         How this really works is that all indented lines must have the same
+         amount of indentation. Indented lines are merged to last
+         unindented line. If line gets longer than 400 characters, it's split.
+       + /SET whois_hide_safe_channel_id ON - Hides the channel ID prefix
+         of !channels in WHOIS replies
+       + When reconnecting to server, say that it can be aborted with
+         /RMRECONNS
+       + /WHOIS -<server tag> is supported now
+       + /SET whois_hide_safe_channel_id - removes the ugly IDs for !channels
+         in /WHOIS (default)
+       + If we can't connect to server using given own IP, show the IP to
+         user in the error message. Should help figuring out wrong
+         /SET hostname or /SERVER -host settings.
+       + If channel has more nicks than /SET channel_max_who_sync, don't ask
+         /WHO list to avoid getting kicked out of server (Max SendQ exceeded).
+       + /LOAD script.pl loads the perl script
+       + /IGNORE -network ignores only on specified network
+       + /SET use_status_window and /SET use_msgs_window make the effects 
+         immediately
+       + Changed the confusing "ircnet" to "network" everywhere
+       + Don't autoget files sent to channels, unless dcc_autoget_masks is set
+       + Added a default "*" target which matches everything on the server,
+         including NULL items associated with it, by jimmy
+       + /UPGRADE now saves server->version
+       + If available, send who set topic and when to irssi-proxy clients
+       + Cleaned up network list: removed smaller networks, added QuakeNet
+       + New default aliases: MANUAL-WINDOWS, EXEMPTLIST and ATAG
+       + Recode support: /RECODE for manipulation of the conversion database.
+         Setting "term_type" has been renamed to "term_charset".
+         /SET recode OFF to disable recode completely.
+         /SET recode_out_default_charset <charset> to specify the default
+         outgoing charset.
+         /SET recode_fallback <charset> to specify a charset that will be
+         used when the normal conversion is failing.
+         /SET recode_transliterate ON to enable character transliteration,
+         so unavailable characters will be transliterated into something 
+         readable
+         <charset> can be almost everything listed by 'iconv -l'
+       + Added transpose_words, capitalize_word, downcase_word, upcase_word
+         key bindings
+       + Avoid accidentaly /VER in a channel, by requiring parameter
+       - Pasted lines weren't added to command history. Some other paste
+         detection fixes
+       - Fixed /BIND escape_char
+       - Fixes for Chinese multibyte characters handling and cursor movement
+         by Wang WenRui
+       - Pasting multibyte chars was buggy, patch by Kuang-che Wu
+       - Fixed handling WHOIS printing once and for all. Everything unknown
+         between "beginning of whois" and "end of whois" events is now printed
+         as whois_special. Removed whois_registered and whois_help, they're
+         printed with whois_special as well.
+       - Don't replace channel key when receiving channel mode numeric. It
+         could be a fake key there.
+       - Don't crash if dcc chated user changes nick
+       - Help files are always lowercased. Make sure /HELP COMMAND works too.
+       - /EXEC crashed with 64bit systems. Patch by Soren Jacobsen
+       - Handle 432 numeric (errorneus nickname) as "nick in use". Fixes
+         problems with ircnet 2.11 ircd when irssi tries to reconnect using
+         UID as nick. Patch by Petr Baudis
+       - /SET -default fixes
+       - /DCC SEND didn't actually use /SET dcc_upload_path
+       - Fixed /WHOIS -yes (Bug 67)
+       - Make /JOIN -tag #channel and /JOIN #channel<space> switch to that
+         channel (Bugs 13 and 93)
+       - Fixed readded (changed) hilights being in config twice, resulted in
+         duplicate hilights or removed hilights coming back (Bug 39)
+       - Fixed messages to @#channel showed *your* nickmode, not the one of
+         the sender (part of Bug 80)
+       - Fixed /KNOCK support
+       - Fixed own nick changes in irssi-proxy
+       - Fixed /HILIGHT -actcolor -mask (Bug 131)
+       - Recognise a param of signal_emit/continue in perl script if it's int
+       - Fixed bug 120 where proxy doesn't set the server_rec->away_reason
+       - Fixed /join -invite -window bug if there is no invite
+       - Fixed bug with time settings where hours actually returned 60*hours
+       - Fix multiple entries for local IP in /etc/hosts prevents connecting,
+         patch by eridius (Bug 167)
+       - Fixed a bug with /me, use the right arguments for 
+         "message irc own_action"
+       - Update our own nickrec->gone flag on /away <reason> or on /away
+       - Fixed output of /hilight (add a space after -levels if any)
+       - Add libtool's -module flag to get built properly on all platforms,
+         by Toby Peterson (Bug 212)
+       - Don't apply emphasis on _foo_^ if it's a nick (Bug 52)
+       - Fix displaying of ctcp userinfo and clientinfo (Bug 222)
+       - Remember alternate_nick and max_whois on reconnect (Bug 181)
+       - Fix tr_TR locale problem for glib2 (still a bug with glib1.2)
+         by David Pashley
+       - Fixed pasting not using the character translation (Bug 151)
+       - Fixed a bug where the channel list to join/rejoin on reconnect
+         gets too long, not all channels will be joined. (Bug 108)
+       - Print glib errors nicely, by David Pashley
+       - Handle ^Z better, by David Pashley
+       - Fixed /eval recursion crashing, by David Pashley
+       - Fix notify with more nicks than max_whois_in_cmd (Bug 257),
+         based on patch by Krzysztof Kowalik (Borys)
+       - Fixed irssiproxy sometimes missing (parts of) lines
+       - Fixed remote /WHOWAS
+       - Parse negative time setting values, makes it possible again to do
+         /SET server_reconnect_time -1 to disable reconnecting
+       - Compile with gcc4
+       - Compile with readonly srcdir
+       - Fixed crash if receiving broken privmsg without source
+         (which bitlbee can send if you msg yourself)
+       - Fixed crash with invalid TERM and termcap
+       - When looking up IP addresses, return random IP instead of the first one
+
+v0.8.9 2003-12-11  Timo Sirainen <tss@iki.fi>
+
+       * Fixes a remote crash with:
+           a) non-x86 hardware (one requiring memory alignmentation)
+           b) script using "gui print text" signal (with x86 hardware too)
+
+       + /SET auto_whowas OFF allows now disabling automatic /whowas when
+         /whois doesn't find a nick (by Borys)
+       - If pasted line starts with command char, treat it as command always. 
+         Paste detection can go on too easily with lagged connections.
+
+v0.8.8 2003-11-23  Timo Sirainen <tss@iki.fi>
+
+       - Just a few fixes to converting old settings automatically
+
+v0.8.7 2003-11-23  Timo Sirainen <tss@iki.fi>
+
+       * Settings changes - we have now "time", "size" and "level" setting
+         types.
+           - Level settings should work the same as before.
+           - Time settings can have units: days, hours, mins, secs,
+             milliseconds (or msecs). The units can be combined and written
+             in shorter form as well, for example "5d 30m 10ms"
+           - Size settings can have units: gbytes, mbytes, kbytes, bytes.
+             They can be written in shorter form as well, eg. "gb" or "g".
+
+         Your existing settings should be converted automatically.
+
+       + Pasting detection. All keys except CR and LF are pasted as-is into
+         prompt in pasting mode.
+
+         /SET paste_detect_time controls how closely each others characters
+         must occur for it to be considered pasting. Paste mode goes on when
+         first pasted CR/LF is found.
+
+         The first line may also contain some command characters. They are
+         executed, but their action in entry line is reverted once pasting
+         is detected.
+
+         What this means in practise is that even if you have TABs (assuming
+         TAB is completion key) in the first pasted line, they get pasted as
+         TABs.
+
+         This detection isn't perfect, so if it annoys you it can be disabled
+         with /SET paste_detect_time 0
+       + If pasting more lines than /SET paste_verify_line_count, irssi asks
+         if you actually want to do that. This should be useful to prevent
+         accidental copy&paste mistakes. Setting it to 0 disables this
+         entirely.
+       + Support for sending SSL certificate to server and optionally verify
+         server's certificate. See the -ssl_* options for /SERVER and
+         /SERVER ADD. Patch by Joel Eriksson.
+       + DCC SERVER support by Mark Trumbull
+       + Support for DCC sending larger files than 2GB if supported by
+         operating system (ie. 64bit file support). Receiving has always
+         been possible, but the statistics were wrong with >4GB files
+         if 64bit support isn't enabled.
+       + Better displaying of DCC file transfer statistics.
+       - Several other minor fixes and enhancements, see ChangeLog
+
+v0.8.6 2002-11-17  Timo Sirainen <tss@iki.fi>
+
+       * Tons of changes, here's only the largest that come to my mind now:
+
+       + SSL support by vjt@users.sf.net
+       + DCC send queues by Heikki Orsila
+       + Better support for !channels
+
 v0.8.4 2002-03-13  Timo Sirainen <tss@iki.fi>
 
        * Continuing to fix my stupid mistakes...
index b1b76bc79e3b6b6c991f9f71b9c899dccbac1eaf..bc2cef99159545b0b1e73839d863e72721fd34ad 100644 (file)
@@ -1,6 +1,161 @@
+ - /whowas server nick doesn't work
+01:18 <@darix> cras: /foreach server /disconnect $tag n8 <-- doesnt work
+ - /hilight -priority is broken
+19:36 [IRCNet] [muzzy] lisää bugeja irssissä, ilmeisesti 
+          uusin versio:  foo splittaa ulos, bar joinaa sisään, bar vaihtaa 
+          nicknamen fooksi, foo splittaa uudestaan ulos -> tulee Glib warning 
+          "is already in split list (how?)" .. :)
+
+04:35 [OPN] [slug] was just wondering if you 
+          had Irssi::keyboard_entry_redirect() on your TODO somewhere near the 
+          top, I'd love the feature
+04:35 [OPN] [slug] or someway to clear the 
+          command buffer, either way is acceptable, just my connection is a 
+          little less than reliable, so I've built myself a script that stores 
+          blowfish passes, all encrypted by one value
+04:36 [OPN] [slug] then I set a single 
+          decryption key, and it keeps it for the irssi session (lost on 
+          /upgrade though), unfortunatly it stays in the command buffer 8]
+
+ - ^I in topicbar breaks things
+14:17 <@darix> cras: this doesnt work: /exec  - -o uname -a
+
+ - "setup changed", or "setup reread" seems to cause crashes.. (with darix)
+02:46 <@fuchs> cras, /server foonet.foo.xy and (recognizing it doesn't connect 
+               fast enough), and so doing /server barnet.foo.xy (both in the 
+               same chatnet) makes irssi joining the net but not rejoining the 
+               channels
+
+
+14:59 <@c0ffee> cras, bug report, mode change compression appears not to work: 
+                13:58 -!- mode/#*cut* [-b+ *cut*] by *cut* (i think it was -b+b)
+21:55 <@L> 17:04.11 <L> /eval /last quit;/clear;/sb goto 10:00;/last -clear;/sb end
+21:55 <@L> 17:04.27 <L> wait after it scrolls and press page up :)
+21:55 <@L> 17:04.48 <L> oh, you can make Irssi behave correctly with /clear
+           again
+
+ - /msg nick@server or nick%host is fully treated as nickname (log, query)
+ - max_whois won't stay with reconnects?
+ - support passive DCC
+ - separate format for privmsg/notice massmessages (anything non-yournick)
+20:45 < Juerd> 19:44 -!- Irssi: critical file channels.c: line 122 
+               (channel_find): assertion
+20:45 < Juerd>           `name != NULL' failed.
+20:45 < Juerd> this happens three times
+20:46 < Juerd> and it happens in a _query_
+20:47 <@Juerd> cras: for some reason irssi doesn't know this happens in a 
+               query, and displays "< Juerd:> foo" in the status window
+ - set TOS field for all connections (DCC especially)
+22:51 [IRCNet] [zhafte] irssi muuten taitaa bugaa jos 
+          pistää ACT oikeaan reunaan, vai onkohan se vain mun terminaali?
+22:52 [IRCNet] [zhafte] menevät välillä päällekkäin 
+          numerot
+ - when using -w password command line parameter, hide the password so it
+   won't show with ps.
+07.06.2002 08:37 #irssi: <@Qrczak> cras: A bug. After /upgrade when being away 
+                                   the awaylog is not being written to.
+21:37 < life> Then you connect. The problem is that irssi connects to the proxy 
+              server and directly afterwards send "USER ..."
+21:37 < life> It has to wait for "HTTP 200 ok" *first*
+04.06.2002 08:54 #irssi: <@Garion> cras: i've seen it several times now - a 
+                                   line of 79 or 80 chars (my win is 80 wide) 
+                                   which has an empty line below it, and that 
+                                   line is not refreshed when I switch to the 
+                                   window with that line in it, thus keeping 1 
+                                   line from the old window in the current 
+                                   window. Very confusing
+21:43 [IRCNet] [HiroP-(~HiroP@p508035EC.dip.t-dialin.net)] Hi there. I just 
+          wanted to tell you that there seems to be a problem when joining 
+          large channels (500+ people) while a regexp ignore is active. I had 
+          one to ignore all server ads
+21:43 [IRCNet] [HiroP-(~HiroP@p508035EC.dip.t-dialin.net)] 1 *: ALL -regexp 
+          -pattern .*erver.*nline.*
+21:43 [IRCNet] [HiroP-(~HiroP@p508035EC.dip.t-dialin.net)] After joining, I 
+          could see the nicklist and 1 or 2 lines of what people were saying. 
+          Then irssi either core-dumped, got disconnected or just sat there 
+          apparently without receiving any more data (lag-counter going up to 
+          several minutes). 
+
+ - %n%_ ei näy lastlogissa
+ - darixin se pingiredirectijuttu
+21:28 < Samus_Aran> cras: /log also shows #Linuux ... which I typod a couple
+                    days ago
+21:30 < Samus_Aran> it doesnt show any of the queries which i closed, though,
+                    jush #channels
+
+ - "show statusbar in empty windows" flag?
+ - statusbar_item_redraw() should just set the size as dirty and calculate
+   it only when really needed.
+ - possible to cache sbar_item->size when nothing else has changed in sbar? 
+   ie. mostly when redrawing.
+ - use_status_window, use_msgs_window sais toimia heti
+ - /msg @#chan<tab>
+
+ - hilight -priority
+ - tab completio jos lisää utf8 juttuja niin ei oikein toimi?
+16:39 <@Qrczak> cras: I'm not sure how exactly to reproduce it but it happens 
+                often. When I jump to the window with a query using Alt-a 
+                (after the other person said something), and close the window 
+                (being brought to the last used window), and don't switch 
+                windows, and then that person says something again, I'm brought 
+                to the new query window automatically (that's of course bad).
+
+21.04.2002 11:59 [immy(immy@beanus.org)] GLib warning: signal_free(script 
+          destroyed) : signal still has 3 references:
+ - jos kickataan nopeasti ja joinaa takas nopeasti chansyncci kaataa
+/WHOssa.. kts. qrczak logi
+ - /set show_server_tags tjsp että näyttäis aina (yhdistä hide_server_tagsin 
+   kanssa?)
+17:35 <@peder> cras: why isnt 'topic = " {sb_topic $topic}"' in the default 
+ - /SBAR topic placement bottom ei toimi??
+ - /SB GOTO -<days> <ts>
+ - /query -immortal so autoclose_queries wouldn't touch them
+ - /SET hiascii_control_chars if 128..128+32 should be treated as control chars
+
+...
+
+ - /exec -out kanavalla ei pelaa silcissä
+ - write about %[-s] etc. to default.theme
+ - away handling is a bit buggy. you do /away;/away reason, irssi remembers
+   the away reason only until it receives "you're no longer away" from the
+   first command.. setting it back to away has then lost the reason.
+ - crash: /exec -msg safari perl -e 'print "A"x600000'"
+ - nick_ nick- _nick nick2 nick3 ...
+17.03.2002 22:25 #irssi.fi: <@Ion> cras: Jos sanon dcc chatissa että /exec - 
+                                   -out cat iso_tiedosto, niin miksi 
+                                   iso_tiedosto jää kesken? :)
+ - /op * valitus vois olla joku parempi kuin "not a good idea"..
+ - mitenkäs tabcompletio completoi taas omaakin nickkiä..?
+ - /hilight -level "public -actions" or something so it wouldn't match
+   actions.
+ - vanhan irssin /upgrades uuteen ennen sitä vaihtoi /set autolog_path:iin
+   $1 ja kaatui?..
+ - /win hide vois pelata vaikka oliskin stickyjä siinä ikkunassa
+ - /SAVE -all?
+14.03.2002 19:10 #irssi: <@fantazja> cras: autoclose_query is also closing (and 
+                                     finishing) dcc chats :/
+ - /window server -sticky:tetyt ikkunat ei aina meinaa tajuta kun serveri
+   yhdistyy?
+ - utf8-tekstitykset bugailee statusbarissa (promptissa)
+ - jos /set reuse_unused_windows off, ja ikkuna näyttäis täysin tyhjälle
+   ja niitä olis vaan 1, niin sen vois kyllä käyttää silti (?)
+ - /set beep_msg_level hilight ei toimi jos on /hilight -word
+
+ - /STATUSBAR xx ENABLE|DISABLE recreates all statusbars which is a bit
+   annoying because some scripts want to do it and input line is cleared
+   because of this..
+ - move /SET hilight_*color to theme
+ - /SET disconnect_timeout - default 2min, 0 = immediately
+ - reconnecting messages are a bit confusing. it prints "removed reconnection"
+   to the server which it's connecting to next.. maybe the whole reconnecting
+   thing should work so that the record stays there until it's connected
+   successfully.
+ - /ignore, /hilight and /lastlog could complain immediately if used
+   regexp is broken. /hilight list could show also if it's broken like
+   /ignore does.
+
  - /UPGRADE:
     - support DCCs
-    - topic time/nick isn't transferred
     - rewrite to work by fork()ing a new process and transfer file handles
       with unix sockets. this would allow the scrollback buffers to be
       transferred with them as well.
        - but DCC chats shouldn't be closed until the chat itself is closed
           - which we can't know really currently, since they don't need
             to be in queries
-       - channels should be closed when they're left
+       - channels should be closed when they're left (they are now, but)
           - /WINDOW CLOSE shouldn't close it immediately, since we'll still
             receive at least the PART message
     - so, log items should know more about what they are exactly, and when
     - support for using strftime() formats (and $tag etc). only problem with
       this is that all the different awaylogs would need to be tracked and
       /CATed when setting yourself unaway
+    - /AWAYLOG could show the current awaylog and optionally reset it
+    - The channel name should be optional there
 
  - Window item placing:
     - !channel vs. !12345channel. it's layout saved with full name, but joined
       the query window with the dcc chat window.
     - closed DCC chats should add temporary window bind to the dcc chat so
       future chats for same nick would use the same window
+    - /JOIN #foo could *optionally* move the channel to active window
+      (default off, it confuses people)
+    - /JOIN -ircnet #foo doesn't jump to #foo like /JOIN #foo does.
 
  - DCC
     - /DCC SEND wildcard support
     - /DCC CLOSE #, /DCC would print the IDs
     - /SET dcc_use_proxy to use IRC proxy for DCC connects as well
     - support for special vars in /SET dcc_download_path, so $N could be used
+    - No way to autoclose dcc chat windows which have been closed by another
+      side.
 
  - Generic chat commands:
     - /MSG /CTCP /ACTION =dcc_chat,#channel
       maybe some multipeople query support? :) /query nick1,nick2 and sending
       text there would send it to both. Seems to work already but receiving
       messages from either nick1 or nick2 don't go to that window..
-    - /^MSG nick creates query with /SET qutocreate_own_query YES
-    - /WHOIS -servertag
     - /BAN: setting of what netmask to use for banning with IPv6 addresses
 
  - Netsplits
 
  - Irssi proxy:
     - doesn't propagate your own nickchanges to other clients
-    - better support for CTCP replies / DCC
+    - list sessions, kill them
+    - /set irssi_proxy_ips <allow connections only from specified IPs>
 
  - Misc IRC related things:
-    - better support for !channels (don't log the ID, show nicer in whois,
-      layout save doesn't work, /win item move !channel)
     - support for mode +q in dancer - also same as +b %xxx modes..
     - properly support RFC-1459 charset (ircnet specific option), eg.
       /QUERY p[ and msgs from p{ aren't placed there.
     - /BAN -ip, -time [<time>] (/ALIAS knockout?)
     - /KICKBAN to support same options than /BAN (would /ALIAS kickban work?)
     - ban list prints "x seconds ago" .. should be x days, mins, hours, ..
-    - /WALL could maybe check if server supports /WALLCHOPS or @#channel?
-      maybe too much trouble figuring out if it can or not.
 
  - Windows:
      - /WINDOW SIZE -sticky, so f.e. /WINDOW BALANCE wouldn't affect it.
     - /BIND -deletes should be saved in config
     - ^W (and some others) don't update cut buffer.
     - default binds: M-d, M-y
-    - capitalize-word (meta-c), downcase-word (meta-l),
-      transpose-words (meta-t), upcase-word (meta-u)                          
-    - UTF-8 support
     - /PASSWORD command that asks you to type the password to entry line and
       would hide it with asterisks, good if people spy on you :)
     - ^R-like history search
+    - Key to execute the command, but not place it in history
+    - Key to remove active entry from command history
+    - Optionally save command history between restarts
 
  - Notify list:
     - showing who's online and who's offline doesn't work properly.
       user comes to irc.
     - "Should we check people in notify list when you're away" option
     - use /WATCH instead of /ISON in servers that support it
+    - Show when the nick was last seen
 
  - Ignores:
     - /IGNORE -ircnet or -tag
       not hide the text.
     - The nick cache stuff just made it slower. Remove it or figure out how
       it could be faster.
+    - combined ignore/hilight thingy, see hilights
 
  - Hilights:
     - /HILIGHT list doesn't print several options. Maybe some generic
     - automatic nick hilighting at beginning of line should be optional,
       like some people would want -word hilighting in it..
     - exceptions
+    - Merged ignores/hilights thingy and maybe even something others ..
+      some first-match-wins table where you could easily add/move stuff.
 
  - Scrollback:
     - Optionally show a "bookmark" (line mark actually, a line full of '-'
     - Fix the flood protection to be aware of max. input buffer, which is
       1024 bytes by default (/STATS d, CF). Now irssi may excess flood when
       sending lots of lines longer than ~200 chars.
-    - Flood protection doesn't count the extra penalty for MODEs and KICKs
-      in ircnet.
+    - IRCNET: Flood protection doesn't count the extra penalty for MODEs
+      and KICKs, also extra penalty should be given in messages (all
+      commands?) for each 100 chars.
 
  - Text buffer:
     - support for printing ALL characters in text buffer, including ^B, ^C,
     - /STATUSBAR prompt DISABLE hangs irssi because there's no input line.
       Add some check to not allow this.
     - /STATUSBAR could list also disabled bars
+    - command to list all available statusbar items
 
  - Server connecting:
     - More verbose connecting
     - bash-style (or whatever it should be called) tab-completion
     - key for reverse completion
     - /MSG <tab> completion shouldn't include queried nick there (optional)
-    - nick completion shouldn't try completing nicks everywhere,
-      like /SET <tab>
     - File completion could guess when it's wanted, word beginning with /
       (not at start of line of course, unless / isn't in cmdchars)
       or ~/ or ./
     - filename completion doesn't work properly if path has spaces
+    - /FORMAT xx <tab>
+    - don't add useless completions to list. eg /RUN nick<tab> shouldn't 
+      work
+    - Priorities to completions. And at least command completion could use
+      it so it'd put last the commands that require chanops/ircops.
+      Requires support in command_bind().
+    - /DCC commands could complete nicks (/dcc close, /dcc get, ..)
+    - check the TODO about nick completion scripts..
 
  - Modules:
     - Figure out module vs. plugin wording, what is what ;)
       irssi .. at least remove the crashing!
     - Irssi::signal_remove() could accept hashes
     - /command parameter parser so it'd be easier to handle -options etc.
-    - when reloading scripts, Irssi::settings_add_int() prints glib errors,
-      while settings_add_str() doesn't
     - Try to get the Makefiles generated so that compiling with GCC would
       always work even if the perl wasn't compiled with GCC..
+    - Irssi::Timeout_add() and input_add()'s data option could be optional
+      and maybe allow multiple parameters
 
  - Bigger code changes:
     - Restructure code tree so that non-IRC chat protocols would be in
       now crash irssi. Also if setting wasn't expected type can cause
       crashes so add proper error checkings everywhere. And is_node_list()
       etc. should be in uppercase..
-    - Would this work..? : command_bind() could specify the parameters
-      it uses, then some generic command parser could parse the commands
-      and options and if all is ok, send the parsed parameters directly with
-      signal_emit() .. I'm just thinking some type checking problems but
-      if all commands would be in format SERVER_REC, WI_ITEM_REC,
-      GHashTable *options, char ** (NULL terminated parameters list) .. ?
-    - support for multiple subcommands in the command parser, like
-      /window name foo level msgs.
     - Channel syncing is evil. Make it optional, and use /USERHOST when
       needed if host isn't known. /BAN at least should do this, and while
-      at it, we could make /IGNORE as well to ignore based on mask.
+      at it, we could make /IGNORE as well to ignore based on mask. Also,
+      if /USERHOST doesn't find anything, use /WHOWAS info.
     - Irssi saves some setting strings to static const char * variables in
       several places.. this works pretty well usually, except when /RELOADing
       config and some "setup changed" signal handler goes and calls some
       them everywhere or figure out something better..
     - Better priority specifying for signals, probably should add
       int priority without limited range.
+    - fix server redirections to handle remote events correctly: very unlikely,
+      but its possible that replies to two remote whoises are received exactly
+      at the same time overlapping each others
+
+ - Commands:
+    - try to get the 0.9 command parser to work..
+    - user definable parameter definitions and how they're handled, like
+      cmsg <target> <colorized-msg> - then there'd be some function called to
+      colorize the third parameter. same for tab completion.
+    - support for multiple subcommands in the command parser, like
+      /window name foo level msgs.
+    - A way to disable some command entirely? eg. not show in completion
+      list or /HELP or anywhere..
 
+ - Read server capabilities from 005 numeric
  - extra spaces after commands don't always work, eg /wii  nick, /help  xx
  - hide channel key in statusbar. This would require a $cmode_nokey or
    something..
  - fe-none doesn't compile with --with-perl-staticlib because it doesn't find
    the ui/textui stuff..
  - we should probably print timestamp even if level contains MSGLEVEL_NEVER,
-   as long as it's not the only level..
+   as long as it's not the only level.. Except when /CATing awaylog we don't
+   want to do that.
  - If /SET print_active_channel is ON, actions still don't show the channel
  - nick's user/host can't be printed for public messages
  - /HELP <alias> should work
    for example could hide/show them. add mouse support for it.
 
  - try profiling the code with /cat filewith10000lines
- - /JOIN #foo could *optionally* move the channel to active window
-   (default off, it confuses people)
  - /SERVER ADD -ircnet foonet bar 6000 pass1,
    /SERVER ADD -ircnet barnet bar 6000 pass2
    dircproxy identifies ircnets based on password
index 33f02000a4bb60cae063148eac3a4eca25b364e5..fd8927c4051bea2ecd46561a2996a460e8ddc085 100644 (file)
 #undef HAVE_CURSES_RESIZETERM
 #undef HAVE_CURSES_WRESIZE
 
-/* terminfo/termcap */
-#undef HAVE_TERMINFO
-
 /* nls */
 #undef ENABLE_NLS
 #undef HAVE_CATGETS
 #undef HAVE_GETTEXT
 #undef HAVE_LC_MESSAGES
 #undef HAVE_STPCPY
+
+/* terminfo/termcap */
+#undef HAVE_TERMINFO
+
+/* If set to 64, enables 64bit off_t for some systems (eg. Linux, Solaris) */
+#undef _FILE_OFFSET_BITS
+
+/* What type should be used for uoff_t */
+#undef UOFF_T_INT
+#undef UOFF_T_LONG
+#undef UOFF_T_LONG_LONG
+
+/* printf()-format for uoff_t, eg. "u" or "lu" or "llu" */
+#undef PRIuUOFF_T
+
index 89ea539a74ceb1ae94e8a61ecf1a3254207a3d64..68c990a278b88edf185a6c71e2cd477b425e9a8c 100644 (file)
@@ -7,7 +7,7 @@ if test -n "`grep '^#undef VERSION' config.h.in`"; then
 fi
 
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(SILC-Client, 0.8.6+)
+AM_INIT_AUTOMAKE(SILC-Client, 0.8.11+)
 
 AM_MAINTAINER_MODE
 
@@ -95,6 +95,13 @@ AC_ARG_WITH(terminfo,
        fi,
        want_terminfo=yes)
 
+AC_ARG_WITH(cuix,
+[  --with-cuix             Use curses ui extended],
+    if test x$withval = xyes; then
+            want_terminfo=no
+            want_cuix=yes
+    fi, want_cuix=no)
+
 AC_ARG_WITH(modules,
 [  --with-modules          Specify what modules to build in binary],
        if test x$withval != xyes -a x$withval != xno; then
@@ -250,6 +257,36 @@ 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 * off_t checks, try to make it 64bit
+AC_DEFINE_UNQUOTED(_FILE_OFFSET_BITS, $preferred_off_t_bits)
+
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(off_t)
+
+if test $ac_cv_sizeof_off_t = 8; then
+  offt_64bit=yes
+else
+  offt_64bit=no
+fi
+
+if test x$ac_cv_sizeof_off_t = x$ac_cv_sizeof_long; then
+  # try to use unsigned long always first
+  AC_DEFINE_UNQUOTED(PRIuUOFF_T, "lu")
+  AC_DEFINE(UOFF_T_LONG, [], [UOFF_T_LONG])
+elif test x$ac_cv_sizeof_off_t = x$ac_cv_sizeof_int; then
+  # next try int
+  AC_DEFINE_UNQUOTED(PRIuUOFF_T, "u")
+  AC_DEFINE(UOFF_T_INT, [], [UOFF_T_INT])
+elif test x$ac_cv_sizeof_off_t = x$ac_cv_sizeof_long_long; then
+  # and finally long long
+  AC_DEFINE_UNQUOTED(PRIuUOFF_T, "llu")
+  AC_DEFINE(UOFF_T_LONG_LONG, [], [UOFF_T_LONG_LONG])
+else
+  AC_ERROR([Couldn't find integer type for off_t])
+fi
+
 dnl **
 dnl ** check for socks
 dnl **
@@ -268,6 +305,10 @@ if test "x$want_socks" = "xyes"; then
        ])
 fi
 
+if test "x$want_cuix" = "xyes"; then
+    AC_DEFINE([HAVE_CUIX], [1] ,[Enable cuix support])
+fi
+
 dnl **
 dnl ** fe-text checks
 dnl **
@@ -687,6 +728,7 @@ AM_CONDITIONAL(HAVE_STATIC_PERL, test "$want_perl" = "static")
 AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes")
 AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes")
 AM_CONDITIONAL(BUILD_SERVERTEST, false)
+AM_CONDITIONAL(USE_CUIX, test "$want_cuix" = "yes")
 
 # move LIBS to PROG_LIBS so they're not tried to be used when linking eg. perl libraries
 PROG_LIBS=$LIBS
@@ -819,6 +861,7 @@ src/perl/ui/Makefile.PL
 src/perl/textui/Makefile.PL
 src/perl/silc/Makefile.PL
 scripts/Makefile
+scripts/examples/Makefile
 docs/Makefile
 docs/help/Makefile
 docs/help/in/Makefile
index 5461b9bf2c3b8d4ada85f39e7aeb9ac4391a9e9e..b0a51e1792ef2e38fd90c31ed406a1609fece096 100644 (file)
@@ -1,6 +1,6 @@
 How to submit a good bug report?
 
-Send them to bugs@irssi.org.
+Send them to bug reporting system in http://bugs.irssi.org/
 
 First you should give the following information:
  - irssi version, if CVS (or devel. tarball) then which day?
diff --git a/apps/irssi/docs/faq.html b/apps/irssi/docs/faq.html
new file mode 100644 (file)
index 0000000..4d1f004
--- /dev/null
@@ -0,0 +1,152 @@
+<h2>FAQ</h2>
+
+<h3>Q: Why doesn't irssi display colors even when ircii etc. displays them?</h3>
+
+<p>A: They force ANSI colors even if terminal doesn't support them. By
+default, irssi uses colors only if terminfo/termcap so says. The correct
+way to fix this would be to change your TERM environment to a value where
+colors work, like xterm-color or color_xterm (eg. <code>TERM=xterm-color
+irssi</code>). If this doesn't help, then use the evil way of <code>/SET
+term_force_colors ON</code>.</p>
+
+
+<h3>Q: How do I easily write text to channel that starts with '/' character?</h3>
+
+<p>A: <code>/ /text</code></p>
+
+
+<h3>Q: Why doesn't irssi update my realname (or whatever) after I change it
+with <code>/SET realname</code> and reconnect with <code>/RECONNECT</code>
+or <code>/SERVER</code>?</h3>
+
+<p>A: Irssi is trying to be too smart. This will be fixed in future, but for
+now you should use <code>/DISCONNECT</code> and <code>/CONNECT</code>.</p>
+
+
+<h3>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?</h3>
+
+<p>A: Two ways. The "good way" to do it is with <code>/DISCONNECT</code>.
+Check the server tags first with <code>/SERVER</code> without giving it any
+parameters, reconnections are those that have tag starting with "recon"
+text. So most probably you're going to do <code>/DISCONNECT recon-1</code>.
+The other way is to remove all the reconnections with
+<code>/RMRECONNS</code>, easier but may remove some connections you actually
+wanted to reconnect (if you used multiple servers..).</p>
+
+
+<h3>Q: How do I add seconds to timestamp?</h3>
+
+<p>A: <code>/FORMAT timestamp {timestamp %%H:%%M:%%S}</code> - and remember
+to add the trailing space :)</p>
+
+
+<h3>Q: Why does irssi say "Irssi: Channel not fully synchronized yet, try
+again after a while" when I try to use /BAN etc?</h3>
+<p>A: Possibly a bug in irssi, or ircd you're using does something that
+irssi didn't really notice. The new code should make this happen far less
+often than before, but one known reason for this is when irssi doesn't
+notice that you were unable to join some channel. Currently however I don't
+know of any such events irssi doesn't know about.</p>
+
+<p>Anyway, if this does happen, do <code>/RAWLOG SAVE ~/rawlog</code> soon
+after joining to channel, and either try to figure out yourself why irssi
+didn't get reply to WHO request, or send the whole log to cras@irssi.org. Note
+that the rawlog is by default only 200 lines and it may not be enough to
+show all needed information, so you might want to do <code>/SET rawlog_lines
+1000</code> or so.</p>
+
+<p><code>MODE +b</code> still works fine though.</p>
+
+
+<h3>Q: Where's the GUI version?</h3>
+
+<p>A: Read
+<a href="http://irssi.org/?page=about">http://irssi.org/?page=about</a></p>
+
+
+<h3>Q: How do I autorejoin channels after being kicked?</h3>
+
+<p>A: That's evil and you shouldn't do it. If you get kicked, you should stay
+out, at least until the channel forgot you existed :) Most channels I've
+joined just ban you if you autorejoin after kick. If you're joined to
+channels who kick people for fun, try changing channels or something.</p>
+
+<p>Anyway, if you REALLY want to do that, and you understand that you're doing
+evilness, you can use the autorejoin.pl script that comes with irssi. You'll
+still need to specify the channels you wish to rejoin with <code>/SET
+autorejoin_channels #chan1 #chan2 ...</code></p>
+
+
+<h3>Q: How do I announce that I'm away/back in all channels I've joined? Or
+how do I change my nick when setting myself away/back?</h3>
+
+<p>A: That's even worse than autorejoin. Who could possibly care every time 
+you come and go? Many channels will kick you for using this, and I for example
+have added several ignores so I'd never need to see these messages. Learn to
+use <code>/AWAY</code> command properly and tell its existence to people
+who don't know about it. <code>/WII yournick</code> shows your away reason
+much better for people who actually want to know if you're there or not.</p>
+
+
+<h3>Q: Why does irssi autojoin on invite by default?</h3>
+
+<p>A: The setting is /SET join_auto_chans_on_invite - it's not the same
+as regular autojoin-on-invite, which irssi doesn't even have. The only
+channels that are joined on invite, are the ones you've added to config
+with /CHANNEL ADD -auto. This is very useful with +i channels when you
+need to first send an invite request to bot, or if you get accidentally
+kicked from channel, the kicker can invite you back immediately.</p>
+
+<p>I don't see any bad side effects with this feature, so it's ON by
+default. I guess someone could start kicking/inviting you all the time
+but server connection shouldn't drop because of that, and you shouldn't
+join channels whose operators are that evil.</p>
+
+
+<h3>Q: How to make UTF-8 support work with irssi?</h3>
+
+<p>A: Make sure your terminal supports UTF-8 (for example, <code>xterm -u8</code>).
+If you use screen, you may have to do <code>screen -U</code>. And in Irssi do
+<code>/SET term_charset utf-8</code>. (for 0.8.9 and older: <code>/SET term_type utf-8</code>)</p>
+
+
+<h3>Q: Will there be /DETACH-like feature?</h3>
+
+<p>A: Maybe. Detach code already is there, attach is just missing :) But I
+don't have much interest in coding it,
+<a href="http://www.gnu.org/software/screen/screen.html">screen</a> and
+<a href="http://dtach.sf.net/">dtach</a> can be used to do it just fine.</p>
+
+
+<h3>Q: How do I run scripts automatically at startup?</h3>
+
+<p>A: Put them into <code>~/.irssi/scripts/autorun/</code> directory. Or
+better would be if you placed them in <code>~/.irssi/scripts/</code> and
+created symlinks to autorun directory (eg. <code>cd
+~/.irssi/scripts/autorun/ ; ln -s ../script.pl .</code>)</p>
+
+
+<h3>Q: How do I execute commands automatically at startup?</h3>
+
+<p>A: Put them into <code>~/.irssi/startup</code> file, each command on its
+own line. The preceding slash (/) is not necessary.</p>
+
+
+<h3>Q: How do I easily edit existing topic?</h3>
+
+<p>A: /TOPIC &lt;tab&gt;</p>
+
+<h3>Q: How can I have /WHOIS replies to active window?</h3>
+
+<p>A: You can disable the status window, or do <code>/WINDOW LEVEL
+-CRAP</code> in it which would also make several other messages show up in
+active window. You can also use a
+<a href="http://dgl.cx/irssi/hack-whois-in-current-window.pl">script</a>.</p>
+
+<h3>Q: How do I add the active network to the statusbar</h3>
+
+<p>A: Modify the window-line in statusbar section in config file to
+<code>window = "{sb $winref:$tag/$T{sbmode $M}}";</code></p>
+
index c836962bb38cc2dbdd2498c7e8dd34f2228d2f63..abf824b4a25c9672e599913e4d969352ea20cf83 100644 (file)
@@ -1,8 +1,6 @@
 # Makefile.am is autogenerated by autogen.sh from Makefile.am.gen
 
-include $(top_srcdir)/Makefile.defines.in
-
-helpdir = $(silc_helpdir)
+helpdir = $(datadir)/irssi/help
 
 help_DATA = \
 @HELPFILES@
diff --git a/apps/irssi/docs/help/in/recode.in b/apps/irssi/docs/help/in/recode.in
new file mode 100644 (file)
index 0000000..8801c56
--- /dev/null
@@ -0,0 +1,59 @@
+
+@SYNTAX:recode@
+
+RECODE
+    %|List the conversion database
+
+RECODE ADD %|[<tag>]|[[<tag>/]<target>] <charset>
+    %|Add an entry to the conversion database (if tag or target is 
+    omitted, the current channel or query will be used). You can specify 
+    the <tag> to have different charsets for the same <target> for
+    different networks. You can omit the target, and specify only the tag
+    if you want to add an entry for the network.
+
+RECODE REMOVE %|[<tag>|<target>]
+    %|Remove an entry from the conversion database (if tag or target is
+    omitted, the current channel or query will be used)
+
+To specify your local charset you have to set term_charset
+
+Example:
+
+/SET term_charset <charset>
+
+To see the recode settings: /SET recode
+
+You can change them with /SET 
+
+Examples:
+
+/SET recode OFF 
+to turn off recode completely
+
+/SET recode_fallback <charset> 
+to set the fallback charset for incoming events
+
+This charset is used if your term_charset is UTF-8
+and the conversion for the target is not set and
+the text is not UTF-8.
+
+/SET recode_out_default_charset <charset> 
+to set the global outgoing charset
+
+When it's set to a charset it will be used
+if no conversion for the target is set.
+
+/SET recode_transliterate ON 
+to enable the global transliteration.
+
+The transliteration is based on your locale settings, 
+if it doesn't work properly your locale settings may be wrong.
+You can enable it per target by adding //TRANSLIT to the <charset>
+
+/SET recode_autodetect_utf8 OFF
+to turn automatic UTF-8 detection off.
+
+Hint: <charset> can be almost everything listed by 'iconv -l'
+
+See also: NETWORK
+
index 7f9b23921264c925bef3f427bb8462ac5e79ef42..d66930fc1f3e3df6208b97325090500ddedc4e51 100644 (file)
 
        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
+       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.
        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>]
+       /NETWORK ADD [-kicks <count>] [-msgs <count>] [-modes <count>]
                    [-whois <count>] [-cmdspeed <ms>] [-cmdmax <count>]
                    [-nick <nick>] [-user <user>] [-realname <name>]
                    [-host <host>] [-autosendcmd <cmd>] <name>
            -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 
+       after connecting to the network. This is useful for automatically 
        identifying yourself to NickServ, for example
 
-       /IRCNET ADD -autosendcmd "/msg NickServ identify secret" freenode
+       /NETWORK ADD -autosendcmd "/msg NickServ identify secret" freenode
 
-       /IRCNET REMOVE <name>
+       /NETWORK REMOVE <name>
 
  5.3 Manually connecting and disconnecting
 
        To connect to a new server, use:
-       /CONNECT [-ircnet <ircnet>] [-host <hostname>] <address>|<ircnet>
+       /CONNECT [-network <network>] [-host <hostname>] <address>|<network>
                 [<port> [<password> [<nick>]]]
 
        If there's no password, set it to -. You can directly connect to
 
  5.4 Server settings
 
-       /SERVER ADD [-auto | -noauto] [-ircnet <ircnet>] [-host <hostname>]
+       /SERVER ADD [-auto | -noauto] [-network <network>] [-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
+           -network: Specify what IRC network this server belongs to
+           -ircnet: Same as -network. Deprecated. Do not use.
            -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
        manually joining to channel without specifying the password.
 
        /CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
-                <channel> <ircnet> [<password>]
+                <channel> <network> [<password>]
 
        With -bots and -botcmd arguments you can automatically send
        commands to someone in channel. This is useful for automatically
        can be removed by setting it to - (or actually, "" works too).
 
        You can remove the channels with
-       /CHANNEL REMOVE <channel> <ircnet>
+       /CHANNEL REMOVE <channel> <network>
 
        /CHANNEL LIST displays list of channels with settings.
        /CHANNEL without any arguments displays list of channels you have
        are in IRC all the time. So I made a bit more featureful notify
        list:
 
-       /NOTIFY [-list] [-away] [-idle [minutes]] <mask> [ircnet [ircnet...]]
+       /NOTIFY [-list] [-away] [-idle [minutes]] <mask> [network [network...]]
 
            -away: Notifies about away-status changes
            -idle: Notifies if idle time is first larger than <minutes>
index cf390a745384e175ca4936719869f2fbc1ed3135..611af9fb0bee54283b881681db82a8105a63e276 100644 (file)
@@ -233,11 +233,27 @@ For example:
 settings_get_str(key)
 settings_get_int(key)
 settings_get_bool(key)
+settings_get_time(key)
+settings_get_level(key)
+settings_get_size(key)
   Return value for setting.
 
+settings_set_str(key, value)
+settings_set_int(key, value)
+settings_set_bool(key, value)
+settings_set_time(key, value)
+settings_set_level(key, value)
+settings_set_size(key, value)
+  Set value for setting.
+  If you change the settings of another module/script with one of these, you
+  must emit a "setup changed" signal afterwards.
+
 settings_add_str(section, key, def)
 settings_add_int(section, key, def)
 settings_add_bool(section, key, def)
+settings_add_time(section, key, def)
+settings_add_level(section, key, def)
+settings_add_size(section, key, def)
   Create new setting.
 
 settings_remove(key)
@@ -274,6 +290,10 @@ timeout_add(msecs, func, data)
   Call `func' every `msecs' milliseconds (1000 = 1 second) with
   parameter `data'. Returns tag which can be used to stop the timeout.
 
+timeout_add_once(msecs, func, data);
+  Call 'func' once after `msecs' milliseconds (1000 = 1 second)
+  with parameter `data'. Returns tag which can be used to stop the timeout.
+
 timeout_remove(tag)
   Remove timeout with tag.
 
@@ -718,7 +738,7 @@ Nick
 Channel::nick_find_mask(mask)
   Find nick mask from nicklist, wildcards allowed.
 
-Channel::nicks(channel)
+Channel::nicks()
   Return a list of all nicks in channel.
 
 Server::nicks_get_same(nick)
@@ -935,6 +955,13 @@ Server::ctcp_send_reply(data)
   is the full raw command to be sent to server, like
     "NOTICE nick :\001VERSION irssi\001"
 
+Server::isupport(name)
+  Returns the value of the named item in the ISUPPORT (005) numeric to the
+  script. If the item is not present returns undef, if the item has no value
+  then "" is returned use defined $server->isupport("name") if you need to
+  check whether a property is present.
+  See http://www.ietf.org/internet-drafts/draft-brocklesby-irc-isupport-01.txt
+  for more information on the ISUPPORT numeric.
 
  *** IRC channels
 
index e893b6d657ab3cf0920955816fad19a28691dbe2..893d666146f0e9117bdb245e6b98ae4513407952 100644 (file)
@@ -84,16 +84,13 @@ server.c:
  "server connect failed", SERVER_REC
  "server disconnected", SERVER_REC
  "server quit", SERVER_REC, char *msg
+ "server sendmsg", SERVER_REC, char *target, char *msg, int target_type
 
 settings.c:
  "setup changed"
  "setup reread", char *fname
  "setup saved", char *fname, int autosaved
 
-signal.c:
-
- "signal", char *name, ...
- "last signal", char *name, ...
 
 IRC core
 --------
@@ -134,6 +131,7 @@ irc.c:
  "server event", SERVER_REC, char *data, char *sender_nick, char *sender_address
  "event "<cmd>, SERVER_REC, char *args, char *sender_nick, char *sender_address
  "default event", SERVER_REC, char *data, char *sender_nick, char *sender_address
+ "whois default event", SERVER_REC, char *args, char *sender_nick, char *sender_address
 
  "server incoming", SERVER_REC, char *data
 
@@ -149,7 +147,7 @@ massjoin.c:
 
 mode-lists.c:
  "ban new", CHANNEL_REC, BAN_REC
- "ban remove", CHANNEL_REC, BAN_REC
+ "ban remove", CHANNEL_REC, BAN_REC, char *setby
 
 modes.c:
  "channel mode changed", CHANNEL_REC, char *setby
@@ -325,11 +323,7 @@ gui-readline.c:
 gui-printtext.c:
  "beep"
 
-SILC
----
-
-silc-channels.c:
- "mime", SERVER_REC, WI_ITEM_REC, char *blob, char *nick, int verified
+Perl
+----
 
-silc-servers.c:
- "mime-send", SERVER_REC, int is_channel, char *to, char *blob, int sign
+"script error", PERL_SCRIPT_REC, char *errormsg
index 2008931ff8bb0c9388c721f2fc380a810e1b29f6..f19b2bb20f3de98fdeeebe4ea84633fffe0cda6a 100644 (file)
@@ -80,7 +80,7 @@ $A .. $Z is important.
    $Z         time of day (hh:mm, can be changed with /SET timestamp_format)
    $$         a literal '$'
 
-   $versiontim          prints time of the irssi version in HHMM format
+   $versiontime         prints time of the irssi version in HHMM format
    $sysname             system name (eg. Linux)
    $sysrelease          system release (eg. 2.2.18)
    $sysarch             system architecture (eg. i686)
diff --git a/apps/irssi/docs/startup-HOWTO-rus.html b/apps/irssi/docs/startup-HOWTO-rus.html
new file mode 100644 (file)
index 0000000..f11bc99
--- /dev/null
@@ -0,0 +1,846 @@
+ <h2>Startup HOWTO</h2>
+
+ <h3>îÏ×ÉÞËÁÍ × Irssi (Á ÎÅ IRC ..)</h3>
+
+ <p>&copy; 2000-2002 by Timo Sirainen, ÒÁÓÐÒÏÓÔÒÁÎÑÅÔÓÑ ÐÏÄ ÌÉÃÅÎÚÉÅÊ
+    <a href="http://www.gnu.org/licenses/fdl.html">GNU FDL</a> 1.1.<br/>
+       îÁ ÒÕÓÓËÉÊ ÑÚÙË ÐÅÒÅ×ÅÄÅÎÏ NiXoiD'ÏÍ (#xakep @ irc.wenet.ru)
+       </p>
+
+
+<p>ïÇÌÁ×ÌÅÎÉÅ Ó ÎÅËÏÔÏÒÙÍÉ ×ÏÐÒÏÓÁÍÉ ÉÚ FAQ, ÎÁ ËÏÔÏÒÙÅ ÄÁÅÔÓÑ ÏÔ×ÅÔ × ÐÁÒÁÇÒÁÆÁÈ:</p>
+
+<ol>
+<li><a href="#c1">äÌÑ ÌÅÎÉ×ÙÈ</a>
+    <ul>
+    <li>õÐÒÁ×ÌÅÎÉÅ ÏËÎÁÍÉ, ÁÎÁÌÏÇÉÞÎÏÅ ircII</li>
+    </ul></li>
+<li><a href="#c2">ïÓÎÏ×Ù ÐÏÌØÚÏ×ÁÔÅÌØÓËÏÇÏ ÉÎÔÅÒÆÅÊÓÁ</a>
+    <ul>
+    <li>òÁÂÏÔÁ Ó "ÒÁÚÄÅÌÅÎÎÙÍÉ" ÏËÎÁÍÉ (Ñ ÔÁË ÐÅÒÅף̠"split windows")</li>
+    <li>ëÁË Ñ ÍÏÇÕ ÌÅÇËÏ ÐÅÒÅËÌÀÞÁÔØÓÑ ÍÅÖÄÕ ÏËÎÁÍÉ?</li>
+    <li>îÏ alt-1 É.Ô.Ä. ÎÅ ÒÁÂÏÔÁÅÔ!</li>
+    </ul></li>
+<li><a href="#c3">á×ÔÏÚÁÈÏÄ ÎÁ ËÁÎÁÌÙ É ÓÅÒ×ÅÒÙ</a>
+    <ul>
+    <li>ëÁË Á×ÔÏÍÁÔÉÞÅÓËÉ ÐÏÄËÌÀÞÁÔØÓÑ Ë ÓÅÒ×ÅÒÁÍ ÐÒÉ ÚÁÐÕÓËÅ?</li>
+    <li>ëÁË Á×ÔÏÍÁÔÉÞÅÓËÉ ÚÁÈÏÄÉÔØ ÎÁ ËÁÎÁÌÙ?</li>
+    <li>ëÁË Á×ÔÏÍÁÔÉÞÅÓËÉ ×ÙÐÏÌÎÑÔØ ËÏÍÁÎÄÙ ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ?</li>
+    </ul></li>
+<li><a href="#c4">îÁÓÔÒÏÊËÁ ÏËÏΠɠÁ×ÔÏÍÁÔÉÞÅÓËÏÅ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÅ ÉÈ ÐÒÉ ÚÁÐÕÓËÅ</a></li>
+<li><a href="#c5">ïËÎÁ status É msgs &amp; ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ</a>
+    <ul>
+    <li>ñ ÈÏÞÕ ÞÔÏÂÙ ÏÔ×ÅÔ ÎÁ /WHOIS ×Ù×ÏÄÉÌÓÑ × ÔÅËÕÝÅÅ ÏËÎÏ</li>
+    <li>ñ ÈÏÞÕ ÞÔÏÂÙ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ×Ù×ÏÄÉÌÉÓØ × ÏÄÎÏÍ ÏËÎÅ</li>
+    </ul></li>
+<li><a href="#c6">ëÁË × irssi ÒÁÂÏÔÁÅÔ ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ</a>
+    <ul>
+    <li>ñ ÐÏÄËÌÀÞÉÌÓÑ Ë ÓÅÒ×ÅÒÕ, ËÏÔÏÒÙÊ ÎÅ ÏÔ×ÅÞÁÅÔ É ÔÅÐÅÒØ irssi ÐÙÔÁÅÔÓÑ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÎÅÍÕ ÓÎÏ×Á É ÓÎÏ×Á. ëÁË ÍÎÅ ÏÓÔÁÎÏ×ÉÔØ ÜÔÏ??</li>
+    <li>ñ ÈÏÞÕ ÏÔÄÅÌØÎÏÅ ÏËÎÏ ÓÔÁÔÕÓÁ É ÓÏÏÂÝÅÎÉÊ ÄÌÑ ËÁÖÄÏÇÏ ÓÅÒ×ÅÒÁ</li>
+    </ul></li>
+<li><a href="#c7">ëÏÍÁÎÄÁ /LASTLOG É ÐÒÏËÒÕÔËÁ ÏËÏÎ</a>
+    <ul>
+    <li>ëÁË ÓÏÈÒÁÎÉÔØ ×ÅÓØ ÔÅËÓÔ ÉÚ ÏËÎÁ × ÆÁÊÌ?</li>
+    </ul></li>
+<li><a href="#c8">÷ÅÄÅÎÉÅ ÌÏÇÏ×</a></li>
+<li><a href="#c9">éÚÍÅÎÅÎÉÅ ËÌÁ×ÉÁÔÕÒÎÙÈ óÏÞÅÔÁÎÉÊ</a>
+    <ul>
+    <li>ëÁË Ñ ÍÏÇÕ ÚÁÓÔÁ×ÉÔØ F1 ÄÅÌÁÔØ ÞÔÏ-ÔÏ?</li>
+    </ul></li>
+<li><a href="#c10">ðÒÏËÓÉ É ÂÏÕÎÓÅÒÙ</a>
+    <ul>
+    <li>þÔÏ ÔÁËÏÅ irssi-proxy?</li>
+    </ul></li>
+<li><a href="#c11">îÁÓÔÒÏÊËÉ Irssi</a></li>
+<li><a href="#c12">óÔÁÔÕÓÂÁÒ</a>
+    <ul>
+    <li>ñ ÚÁÇÒÕÚÉÌ ÓËÒÉÐÔ ÄÌÑ ÓÔÁÔÕÓÂÁÒÁ, ÎÏ ÅÇÏ ÎÉÇÄÅ ÎÅ ×ÉÄÎÏ!</li>
+    </ul></li>
+</ol>
+
+<h3><a id="c1">1. äÌÑ ÌÅÎÉ×ÙÈ</a></h3>
+
+<p>îÅÓËÏÌØËÏ ÐÏÌÅÚÎÙÈ ÎÁÓÔÒÏÅË ÐÏ ÕÍÏÌÞÁÎÉÀ:</p>
+
+<p>åÓÌÉ ÎÅ ÒÁÂÏÔÁÀÔ Ã×ÅÔÁ É ×Ù ÎÅ ÓÏÂÉÒÁÅÔÅÓØ ÉÓÐÏÌØÚÏ×ÁÔØ VT-ÎÅÓÏ×ÍÅÓÔÉÍÙÊ ÔÅÒÍÉÎÁÌ, ÔÏ ÐÒÏÓÔÏ ××ÅÄÉÔÅ:</p>
+
+<pre>
+/SET term_force_colors ON
+</pre>
+
+<p>åÓÌÉ ×Ù ÈÏÔÉÔÅ ÞÔÏÂÙ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ×Ù×ÏÄÉÌÉÓØ × ÏÄÎÏÍ ÏËÎÅ:</p>
+
+<pre>
+/SET autocreate_own_query OFF
+/SET autocreate_query_level DCCMSGS
+/SET use_status_window OFF
+/SET use_msgs_window ON
+</pre>
+
+<p>þÔÏÂÙ ÏËÎÁ Á×ÔÏÍÁÔÉÞÅÓËÉ ÎÅ ÚÁËÒÙ×ÁÌÉÓØ ËÏÇÄÁ ×Ù ÐÏËÉÄÁÅÔÅ ËÁÎÁÌ(<code>/PART</code>)ÉÌÉ ÐÒÉ×ÁÔ
+(<code>/UNQUERY</code>):</p>
+
+<pre>
+/SET autoclose_windows OFF
+/SET reuse_unused_windows ON
+</pre>
+
+<p>þÔÏÂÙ ÕÐÒÁ×ÌÅÎÉÅ ÏËÎÁÍÉ × irssi ÂÙÌÏ ÐÏÈÏÖÅ ÎÁ ircII ××ÅÄÉÔÅ ÜÔÉ ËÏÍÁÎÄÙ:</p>
+
+<pre>
+/SET autocreate_own_query OFF
+/SET autocreate_query_level NONE
+/SET use_status_window OFF
+/SET use_msgs_window OFF
+/SET reuse_unused_windows ON
+/SET windows_auto_renumber OFF
+
+/SET autostick_split_windows OFF
+/SET autoclose_windows OFF
+/SET print_active_channel ON
+</pre>
+
+<p>÷ÏÔ ÐÒÉÍÅÒ ÄÏÂÁ×ÌÅÎÉÑ ÓÅÒ×ÅÒÏ×:</p>
+
+<p>(ÓÅÔØ OFTC, ÉÄÅÎÔÉÆÉÃÉÒÏ×ÁÔØÓÑ ÞÅÒÅÚ nickserv É ÖÄÁÔØ 2 ÓÅËÕÎÄÙ ÐÅÒÅÄ ÚÁÈÏÄÏÍ ÎÁ ËÁÎÁÌÙ)</p>
+
+<pre>
+/IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait 2000" OFTC
+</pre>
+
+<p>ôÅÐÅÒØ ÄÏÂÁ×ÌÅÎÉÅ ÎÅÓËÏÌØËÉÈ ÓÅÒ×ÅÒÏ× Ë ÒÁÚÎÙÍ ÓÅÔÑÍ (IRC-ÓÅÔØ ÄÌÑ ÎÉÈ ÕÖÅ ÕÓÔÁÎÏ×ÌÅÎÁ),
+ irc.kpnqwest.fi ÉÓÐÏÌØÚÕÅÔÓÑ ÐÏ ÄÅÆÏÌÔÕ ÄÌÑ IRCNet ÎÏ ÅÓÌÉ ÏΠÎÅ ÄÏÓÔÕÐÅÎ, ÔÏ irssi ÂÕÄÅÔ ÐÙÔÁÔØÓÑ ÐÏÄËÌÀÞÉÔØÓÑ Ë 
+irc.funet.fi:</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>á×ÔÏÚÁÈÏÄ ÎÁ ËÁÎÁÌÙ ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ Ë ÓÅÒ×ÅÒÕ É ÏÐ-ÚÁÐÒÏÓ ÂÏÔÁ ÐÒÉ ÚÁÈÏÄÅ ÎÁ efnet/#irssi:</p>
+
+<pre>
+/CHANNEL ADD -auto #irssi ircnet
+/CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
+</pre>
+
+þÔÏÂÙ ÓÔÒÏËÉ, ÓÏÄÅÒÖÁÝÉÅ ÷ÁÛ ÎÉË ÐÏÄÓ×ÅÞÉ×ÁÌÉÓØ:
+
+<pre>
+/HILIGHT ×ÁÛ_ÎÉË
+</pre>
+
+<h3><a id="c2">2. ïÓÎÏ×Ù ÐÏÌØÚÏ×ÁÔÅÌØÓËÏÇÏ ÉÎÔÅÒÆÅÊÓÁ</a></h3>
+
+<p>äÌÑ ÓËÒÏÌÌÉÎÇÁ ÓÏÄÅÒÖÉÍÏÇÏ ÏËÏΠÉÓÐÏÌØÚÕÊÔÅ PgUp É PgDown. åÓÌÉ ÏÎÉ ÎÅ ÒÁÂÏÔÁÀÔ, ÉÓÐÏÌØÚÕÊÔÅ ËÎÏÐËÉ Meta-p É Meta-n.
+ þÔÏÂÙ ÐÅÒÅÓËÏÞÉÔØ × ÎÁÞÁÌÏ ÉÌÉ ËÏÎÅàÂÕÆÅÒÁ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÙ <code>/SB HOME</code> É <code>/SB END</code>.</p>
+
+<p>ðÏ ÕÍÏÌÞÁÎÉÀ irssi ÉÓÐÏÌØÚÕÅÔ ÄÌÑ ×ÓÅÇÏ "ÓËÒÙÔÙÅ ÏËÎÁ". óËÒÙÔÏÅ ÏËÎÏ ÓÏÚÄÁÅÔÓÑ ËÁÖÄÙÊ ÒÁÚ ËÏÇÄÁ ×Ù ÚÁÈÏÄÉÔÅ(<code>/JOIN</code>) ÎÁ ËÁÎÁÌ ÉÌÉ ÓÏÚÄÁÅÔÅ ÐÒÉ×ÁÔ(<code>/QUERY</code>)
+Ó ËÅÍ-ÔÏ. åÓÔØ ÎÅÓËÏÌØËÏ ÓÐÏÓÏÂÏ× ÐÅÒÅËÌÀÞÅÎÉÑ ÍÅÖÄÕ ÜÔÉÍÉ ÏËÎÁÍÉ:</p>
+
+<pre>
+Meta-1, Meta-2, .. Meta-0 - ðÅÒÅËÌÀÞÅÎÉÅ ÍÅÖÄÕ ÏËÎÁÍÉ 1-10
+Meta-q .. Meta-o          - ðÅÒÅËÌÀÞÅÎÉÅ ÍÅÖÄÕ ÏËÎÁÍÉ 11-19
+/WINDOW &lt;ÎÏÍÅÒ&gt;          - ðÅÒÅËÌÀÞÅÎÉÅ ÎÁ ÏËÎÏ Ó ÚÁÄÁÎÎÙÍ ÎÏÍÅÒÏÍ
+Ctrl-P, Ctrl-N            - ðÅÒÅËÌÀÞÅÎÉÅ Ë ÐÒÅÄÙÄÕÝÅÍÕ/ÓÌÅÄÕÀÝÅÍÕ ÏËÎÕ
+</pre>
+
+<p>ðÒÏÓÔÅÊÛÉÊ ÓÐÏÓÏ ÐÅÒÅËÌÀÞÅÎÉÑ - ÜÔÏ Meta-ÎÏÍÅÒ. þÔÏ ÔÁËÏÅ Meta? 
+äÌÑ ÎÅËÏÔÏÒÙÈ ÔÅÒÍÉÎÁÌÏ× ÜÔÏ ALT. åÓÌÉ Õ ×ÁÓ windows-ÓÏ×ÍÅÓÔÉÍÁÑ ËÌÁ×ÉÁÔÕÒÁ, ÔÏ ÜÔÏ ÔÁË-ÖÅ ÍÏÖÅÔ ÂÙÔØ ÌÅ×ÁÑ ËÎÏÐËÁ windows. åÓÌÉ ÏÎÉ ÎÅ ÒÁÂÏÔÁÀÔ, ÔÏ ×ÁÍ ÐÒÉÄÅÔÓÑ ÎÁÓÔÒÏÉÔØ ÎÅËÏÔÏÒÙÅ X-ÒÅÓÕÒÓÙ
+(ÜÔÏ ÒÁÂÏÔÁÅÔ ËÁË × xterm ÔÁË É × rxvt):</p>   
+
+<pre>
+XTerm*eightBitInput:   false
+XTerm*metaSendsEscape: true
+</pre>
+
+<p>÷ rxvt ×Ù ÔÁË-ÖÅ ÍÏÖÅÔÅ ÕËÁÚÁÔØ ËÁËÁÑ ËÎÏÐËÁ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ËÎÏÐËÅ meta, ÔÁË ÞÔÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ Alt ×ÍÅÓÔÏ Win ÄÏÐÉÛÉÔÅ ÜÔÏ × ÆÁÊÌ Ó ÒÅÓÕÒÓÁÍÉ:</p>
+
+<pre>
+rxvt*modifier: alt
+</pre>
+
+<p>÷Ù ÔÁË-ÖÅ ÍÏÖÅÔÅ ÓÄÅÌÁÔØ ÜÔÏ ÐÒÉ ÐÏÍÏÝÉ xmodmap:</p>
+
+<pre>
+xmodmap -e "keysym Alt_L = Meta_L Alt_L"
+</pre>
+
+<p>ôÁË ËÁË-ÖÅ ÕÓÔÁÎÏ×ÉÔØ ÜÔÉ X-ÒÅÓÕÒÓÙ? äÌÑ Debian'Á, ÜÔÏ ÆÁÊÌ
+<code>/etc/X11/Xresources/xterm</code>, × ËÏÔÏÒÙÊ ×Ù ÍÏÖÅÔÅ ÉÈ ÚÁÓÕÎÕÔØ É ÏÎÉ ÂÕÄÕÔ Á×ÔÏÍÁÔÉÞÅÓËÉ ÞÉÔÁÔØÓÑ ÐÒÉ ÓÔÁÒÔÅ ÉËÓÏ×. æÁÊÌÙ <code>~/.Xresources</code> É 
+<code>~/.Xdefaults</code> ÔÁË-ÖÅ ÄÏÌÖÎÙ ÒÁÂÏÔÁÔØ. åÓÌÉ ÎÉÞÅÇÏ ÉÚ ×ÙÛÅÐÅÒÅÞÉÓÌÅÎÎÏÇÏ ÎÅ ÒÁÂÏÔÁÅÔ, ÔÏ ÐÒÏÓÔÏ ÓËÏÐÉÒÕÊÔÅ ÉÈ × <code>~/.Xresources</code>
+É ÚÁÇÒÕÚÉÔÅ ËÏÍÁÎÄÏÊ <code>xrdb -merge ~/.Xresources</code>. 
+éÚÍÅÎÅÎÉÑ ÎÁÞÉÎÁÀÔ ÄÅÊÓÔ×Ï×ÁÔØ ÔÏÌØËÏ × ÚÁÎÏ×Ï ÚÁÐÕÝÅÎÎÏÍ ÔÅÒÍÉÎÁÌÅ.</p> 
+
+<p>íÎÏÇÉÅ SSH ËÌÉÅÎÔÙ ÐÏÄ Windows ÔÁË ÖÅ ÎÅ ÒÁÚÒÅÛÁÀÔ ÉÓÐÏÌØÚÏ×ÁÔØ ËÎÏÐËÕ ALT. ðÒÅËÒÁÓÎÙÊ ËÌÉÅÎÔ, ËÏÔÏÒÙÊ ÐÏÚ×ÏÌÑÅÔ ÄÅÌÁÔØ ÜÔÏ -  putty, ×Ù ÍÏÖÅÔÅ ÓËÁÞÁÔØ ÅÇÏ Ó
+<a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">
+http://www.chiark.greenend.org.uk/~sgtatham/putty/</a>.</p>
+
+<p>ôÁË-ÖÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÒÁÚÄÅÌÅÎÉÅ ÏËÏÎ. ÷ÏÔ ËÏÍÁÎÄÙ, ËÏÔÏÒÙÅ ÐÏÚ×ÏÌÑÀÔ ÜÔÏ ÓÄÅÌÁÔØ:</p>
+
+<pre>
+/WINDOW NEW                    - óÏÚÄÁÔØ ÎÏ×ÏÅ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ
+/WINDOW NEW HIDE               - óÏÚÄÁÔØ ÎÏ×ÏÅ ÓËÒÙÔÏÅ ÏËÎÏ
+/WINDOW CLOSE                  - úÁËÒÙÔØ ÒÁÚÄÅÌÅÎÎÏÅ ÉÌÉ ÓËÒÙÔÏÅ ÏËÎÏ
+
+/WINDOW HIDE [&lt;number&gt;|&lt;name&gt;] - óÄÅÌÁÔØ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ ÓËÒÙÔÙÍ
+/WINDOW SHOW &lt;number&gt;|&lt;name&gt;   - óÄÅÌÁÔØ ÓËÒÙÔÏÅ ÏËÎÏ ÒÁÚÄÅÌÅÎÎÙÍ
+
+/WINDOW SHRINK [&lt;lines&gt;]       - õÍÅÎØÛÉÔØ ÁËÔÉ×ÎÏÅ ÏËÎÏ
+/WINDOW GROW [&lt;lines&gt;]         - õ×ÅÌÉÞÉÔØ ÁËÔÉ×ÎÏÅ ÏËÎÏ
+/WINDOW BALANCE                - óÂÁÌÁÎÓÉÒÏ×ÁÔØ ÒÁÚÍÅÒÙ ×ÓÅÈ ÒÁÚÄÅÌÅÎÎÙÈ ÏËÏÎ
+</pre>
+
+<p>ðÏ ÕÍÏÌÞÁÎÉÀ Irssi ÉÓÐÏÌØÚÕÅÔ "ÐÒÉËÌÅÉ×ÁÎÉÅ ÏËÏÎ". üÔÏ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ÏËÎÏ, ÓÏÚÄÁÎÎÏÅ ×ÎÕÔÒÉ ÒÁÚÄÅÌÅÎÎÏÇÏ ÏËÎÁ ÎÅ ÍÏÖÅÔ ÂÙÔØ ÐÅÒÅÍÅÝÅÎÏ ÂÅÚ ÎÅËÏÔÏÒÏÇÏ ÇÅÍÏÒÁ :). îÁÐÒÉÍÅÒ Õ ×ÁÓ ÍÏÖÅÔ ÂÙÔØ ÓÌÅÄÕÀÝÅÅ ÒÁÓÐÏÌÏÖÅÎÉÅ ÏËÏÎ:</p>
+
+<pre>
+Split window 1: win#1 - Status window, win#2 - ïËÎÏ ÓÏÏÂÝÅÎÉÊ
+Split window 2: win#3 - ircnet/#channel1, win#4 - ircnet/#channel2
+Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
+</pre>
+
+<p>ëÏÇÄÁ ×Ù × ÏËÎÅ win#1 ÎÁÖÉÍÁÅÔÅ ALT-6, irssi ÐÅÒÅËÌÀÞÁÅÔÓÑ ÎÁ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ
+#3 É ÐÅÒÅÍÅÝÁÅÔ ËÁÎÁÌ efnet/#channel2 × ÁËÔÉ×ÎÏÅ ÏËÎÏ.</p>
+
+<p>ðÒÉ "ÎÅÚÁËÒÅÐÌ£ÎÎÏÍ" ×ÁÒÉÁÎÔÅ ÏËÎÁ ÎÅ ÉÍÅÀÔ ÎÉËÁËÏÊ Ó×ÑÚÉ Ó ÒÁÚÄÅÌÅÎÎÙÍÉ ÏËÎÁÍÉ 
+É ÎÁÖÁÔÉÅ ALT-6 × ÏËÎÅ win#1 ÐÅÒÅÍÅÝÁÅÔ ÏËÎÏ win#6 × ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ 1
+É ÄÅÌÁÅÔ ÅÇÏ ÁËÔÉ×ÎÙÍ, ÉÓËÌÀÞÅÎÉÅ ÍÏÖÅÔ ÂÙÔØ ËÏÇÄÁ ÏËÎÏ win#6 ÕÖÅ ×ÉÄÉÍÏ × ËÁËÏÍ-ÔÏ ÄÒÕÇÏÍ
+ÒÁÚÄÅÌÅÎÎÏÍ ÏËÎÅ, irssi ÐÒÏÓÔÏ ÐÅÒÅËÌÀÞÁÅÔÓÑ Ë ÜÔÏÍÕ ÒÁÚÄÅÌÅÎÎÏÍÕ ÏËÎÕ. ôÁËÏÊ ÍÅÔÏÄ ÐÅÒÅËÌÀÞÅÎÉÑ ÍÅÖÄÕ ÏËÎÁÍÉ ÐÒÉÍÅÎÑÅÔÓÑ × ircII É ÅÓÌÉ ÏΠ×ÁÍ ÐÏÎÒÁ×ÉÌÓÑ ÔÏ ×Ù ÍÏÖÅÔÅ ÁËÔÉ×ÉÚÉÒÏ×ÁÔØ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ</p>
+
+<pre>
+/SET autostick_split_windows OFF
+</pre>
+
+<p>ëÁÖÄÏÅ ÏËÎÏ ×ÎÕÔÒÉ ÓÅÂÑ ÍÏÖÅÔ ÓÏÄÅÒÖÁÔØ ÍÎÏÇÏ ËÁÎÁÌÏ×, ÐÒÉ×ÁÔÏ× É ÄÒÕÇÉÈ "×ÅÝÅÊ". åÓÌÉ ×Ù ×ÏÏÂÝÅ ÎÅ ÌÀÂÉÔÅ ÏËÎÁ, ÔÏ ×Ù ÍÏÖÅÔÅ ÏÔÍÅÎÉÔØ ÉÈ ËÏÍÁÎÄÏÊ</p>
+
+<pre>
+/SET autocreate_windows OFF            [format c: ÎÁÄ£ÖÎÅÅ ;) - ÐÒÉÍ. ÐÅÒÅ×.]
+</pre>
+
+<p>é ÅÓÌÉ ×Ù ÄÅÒÖÉÔÅ ×ÓÅ ËÁÎÁÌÙ × ÏÄÎÏÍ ÏËÎÅ, ÔÏ ×ÁÍ ÎÁ×ÅÒÎÏÅ ÚÁÈÏÞÅÔÓÑ ÞÔÏÂÙ ÉÍÑ ËÁÎÁÌÁ ×Ù×ÏÄÉÌÏÓØ × ËÁÖÄÏÍ ÓÏÏÂÝÅÎÉÉ:</p>
+
+<pre>
+/SET print_active_channel ON
+</pre>
+
+<p>åÓÌÉ ×Ù ÈÏÔÉÔÅ ÓÇÒÕÐÐÉÒÏ×ÁÔØ × ËÁËÏÅ-ÔÏ ÏËÎÏ ÔÏÌØËÏ ÎÅËÏÔÏÒÙÅ ËÁÎÁÌÙ ÉÌÉ ÐÒÉ×ÁÔÙ, ÔÏ ÉÓÐÏÌØÚÕÊÔÅ ÜÔÉ ËÏÍÁÎÄÙ:</p>
+
+<pre>
+/JOIN -window #channel
+/QUERY -window nick
+</pre>
+
+<h3><a id="c3">3. á×ÔÏÚÁÈÏÄ ÎÁ ËÁÎÁÌÙ É ÓÅÒ×ÅÒÙ</a></h3>
+
+<p>÷ Irssi ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ éíèï ÏÞÅÎØ ÈÏÒÏÛÁÑ :). äÁÖÅ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÏÂÝÁÔØÓÑ ÔÏÌØËÏ × ÏÄÎÏÊ ÓÅÔÉ, ÔÏ ÏÞÅÎØ ÕÄÏÂÎÏ ÓÇÒÕÐÐÉÒÏ×ÁÔØ ×ÓÅ ÓÅÒ×ÅÒÙ ÜÔÏÊ ÓÅÔÉ × ÏÄÎÕ ÇÒÕÐÐÕ Ô.Ë. ÜÔÏ ÐÏÍÏÇÁÅÔ × ÓÌÕÞÁÅ ÎÅ×ÏÚÍÏÖÎÏÓÔÉ ÓÏÅÄÉÎÅÎÉÑ Ó ÇÌÁ×ÎÙÍ ÓÅÒ×ÅÒÏÍ É × ÎÅËÏÔÏÒÙÈ ÄÒÕÇÉÈ ÓÌÕÞÁÑÈ :). 
+äÏÐÏÌÎÉÔÅÌØÎÕÀ ÉÎÆÏÒÍÁÃÉÀ Ï ÜÆÆÅËÔÉ×ÎÏÍ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÍÎÏÇÏÓÅÒ×ÅÒÎÏÊ ÐÏÄÄÅÒÖËÉ ÓÍÏÔÒÉÔÅ × ÇÌÁ×Å 6.</p>
+
+<p>äÌÑ ÎÁÞÁÌÁ ×ÁÍ ÎÕÖÎÏ ÕÓÔÁÎÏ×ÉÔØ Ó×ÏÀ IRC-ÓÅÔØ, ÄÌÑ ÜÔÏÇÏ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/IRCNET</code>,
+ÞÔÏÂÙ ÕÂÅÄÉÔÓÑ, ÞÔÏ ÏÎÁ ÅÝ£ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ. åÓÌÉ ÏÎÁ ÎÅ ÕÓÔÁÎÏ×ÌÅÎÁ, ÔÏ ××ÅÄÉÔÅ <code>/IRCNET ADD
+ÉÍÑ_ÓÅÔÉ</code>. åÓÌÉ ×Ù ÈÏÔÉÔÅ, ÞÔÏÂÙ ËÁËÉÅ-ÔÏ ËÏÍÁÎÄÙ Á×ÔÏÍÁÔÉÞÅÓËÉ ×ÙÐÏÌÎÑÌÉÓØ ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ Ë ÜÔÏÊ ÓÅÔÉ, ÔÏ ×ÏÓÐÏÌØÚÕÊÔÅÓØ ÏÐÃÉÅÊ <code>-autosendcmd</code>.
+÷ÏÔ ÎÅËÏÔÏÒÙÅ ÐÒÉÍÅÒÙ:</p>
+
+<pre>
+/IRCNET ADD -autosendcmd '^msg bot invite' ircnet
+/IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait 2000" OFTC
+</pre>
+
+<p>ðÏÓÌÅ ÜÔÏÇÏ ×Ù ÄÏÌÖÎÙ ÄÏÂÁ×ÉÔØ Ë ÜÔÏÊ ÓÅÔÉ ÓÅÒ×ÅÒÙ. îÁÐÒÉÍÅÒ:</p>
+
+<pre>
+/SERVER ADD -auto -ircnet ircnet irc.kpnqwest.fi 6667
+/SERVER ADD -auto -ircnet worknet irc.mycompany.com 6667 ÐÁÒÏÌØ
+</pre>
+
+<p>ïÐÃÉÑ <code>-auto</code> ÕËÁÚÙ×ÁÅÔ, ÞÔÏ Ë ÜÔÏÍÕ ÓÅÒ×ÅÒÕ ÎÕÖÎÏ Á×ÔÏÍÁÔÉÞÅÓËÉ ÐÏÄËÌÀÞÁÔØÓÑ ÐÒÉ ÚÁÐÕÓËÅ. 
+÷Ù ÎÅ ÄÏÌÖÎÙ ÐÏÍÅÞÁÔØ ÄÒÕÇÉÅ ÓÅÒ×ÅÒÙ ÔÏÊ-ÖÅ ÓÅÔÉ ÏÐÃÉÅÊ <code>-auto</code> - Irssi Á×ÔÏÍÁÔÉÞÅÓËÉ Ë ÎÉÍ ÐÏÄËÌÀÞÉÔÓÑ, ÅÓÌÉ ÓÅÒ×ÅÒ ÐÏÍÅÞÅÎÎÙÊ <code>-auto</code> ÎÅÄÏÓÔÕÐÅÎ.</p>
+
+<p>é ÎÁËÏÎÅàËÁÎÁÌÙ:</p>
+
+<pre>
+/CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
+/CHANNEL ADD -auto #secret ircnet password
+</pre>
+
+<p>ïÐÃÉÉ <code>-bots</code> É <code>-botcmd</code> ÔÒÅÂÕÀÔ ÎÅÂÏÌØÛÏÇÏ ÐÏÑÓÎÅÎÉÑ. 
+ïÎÉ ÉÓÐÏÌØÚÕÀÔÓÑ ÄÌÑ ÔÏÇÏ, ÞÔÏÂÙ Á×ÔÏÍÁÔÉÞÅÓËÉ ÄÁ×ÁÔØ ËÏÍÁÎÄÙ ÂÏÔÕ ÐÒÉ ÚÁÈÏÄÅ ÎÁ ËÁÎÁÌ, 
+ÏÂÙÞÎÏ ÄÌÑ Á×ÔÏÍÁÔÉÞÅÓËÏÇÏ ÐÏÌÕÞÅÎÉÑ ÏÐÁ. ÷Ù ÍÏÖÅÔÅ ÚÁÄÁÔØ ÍÎÏÇÏ ÍÁÓÏË ÂÏÔÏ× ÐÒÉ ÐÏÍÏÝÉ ÏÐÃÉÉ 
+<code>-bots</code>, ÒÁÚÄÅÌÅÎÎÏÊ ÐÒÏÂÅÌÁÍÉ (ÎÅ ÚÁÂÕÄØÔÅ ×ÚÑÔØ ÜÔÕ ÓÔÒÏËÕ × ËÁ×ÙÞÅË). ðÅÒÅÍÅÎÎÁÑ $0 × ÏÐÃÉÉ 
+<code>-botcmd</code> ÕËÁÚÙ×ÁÅÔ ÎÁ ÐÅÒ×ÏÇÏ ÂÏÔÁ × ÓÐÉÓËÅ ÎÁÊÄÅÎÎÙÈ. åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÍÁÓËÉ ÄÌÑ ÂÏÔÏ× (ÎÁÐÒÉÍÅÒ ÅÓÌÉ ÂÏÔ ×ÓÅÇÄÁ ÓÉÄÉÔ ÐÏÄ ÏÄÎÉÍ ÎÉËÏÍ) 
+×Ù ÍÏÖÅÔÅ ÕËÁÚÁÔØ ÔÏÌØËÏ ÏÐÃÉÀ <code>-botcmd</code> É ËÏÍÁÎÄÕ.</p>
+
+<h3><a id="c4">4. îÁÓÔÒÏÊËÁ ÏËÏΠɠÁ×ÔÏÍÁÔÉÞÅÓËÏÅ ×ÏÓÓÔÁÎÏ×ÌÅÎÉÅ ÐÒÉ ÚÁÐÕÓËÅ</a></h3>
+
+<p>äÌÑ ÎÁÞÁÌÁ ÓÏÚÄÁÊÔÅ ÎÕÖÎÙÅ ÏËÎÁ(ÐÏÄËÌÀÞÉÔÅÓØ Ë ÎÕÖÎÙÍ ÓÅÒ×ÅÒÁÍ, ËÁÎÁÌÁÍ É.Ô.Ä.). 
+äÌÑ ÐÅÒÅÍÅÝÅÎÉÑ ÏËÏΠÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÉÅ ËÏÍÁÎÄÙ:</p>
+
+<pre>
+/WINDOW MOVE LEFT/RIGHT/ÎÏÍÅÒ    - ÐÅÒÅÍÅÓÔÉÔØ ÏËÎÏ ×ÌÅ×Ï, ×ÐÒÁ×Ï ÉÌÉ ÎÁ ÕËÁÚÁÎÎÙÊ ÎÏÍÅÒ
+/WINDOW ITEM MOVE &lt;ÎÏÍÅÒ&gt;|&lt;ÉÍÑ&gt; - ÐÅÒÅÍÅÓÔÉÔØ ËÁÎÁÌ ÉÌÉ ÐÒÉ×ÁÔ × ÄÒÕÇÏÅ ÏËÎÏ
+</pre>
+
+<p>ëÏÇÄÁ ×Ó£ ×ÙÇÌÑÄÉÔ ÔÁË, ËÁË ×Ù ÈÏÔÉÔÅ, ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/LAYOUT SAVE</code>
+ (É <code>/SAVE</code>, ÅÓÌÉ ÎÅ ×ËÌÀÞÅÎÏ Á×ÔÏÓÏÈÒÁÎÅÎÉÅ) É ËÏÇÄÁ ×Ù × ÓÌÅÄÕÀÝÉÊ ÒÁÚ ÚÁÐÕÓÔÉÔÅ irssi, ÔÏ ÏΠ×ÓÐÏÍÎÉÔ ÐÏÚÉÃÉÉ ÓÏÈÒÁÎÅÎÎÙÈ ÏËÏÎ. 
+ üÔÏ "ÚÁÐÏÍÉÎÁÎÉÅ" ÎÅ ÏÚÎÁÞÁÅÔ, ÞÔÏ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ËÏÍÁÎÄÙ <code>/LAYOUT SAVE</code> ÂÕÄÅÔ ÐÒÉ×ÏÄÉÔØ Ë Á×ÔÏÍÁÔÉÞÅÓËÏÍÕ ÐÏÄËÌÀÞÅÎÉÀ Ë ÓÅÒ×ÅÒÁÍ É ÚÁÈÏÄÕ ÎÁ ËÁÎÁÌÙ, 
+ ÄÌÑ ÜÔÏÇÏ ×Ù ÄÏÌÖÎÙ ÉÓÐÏÌØÚÏ×ÁÔØ ËÏÍÁÎÄÙ <code>/SERVER ADD -auto</code> É <code>/CHANNEL ADD -auto</code>.</p>
+
+<p>þÔÏÂÙ ÉÚÍÅÎÉÔØ ÓÏÈÒÁÎÅÎÎÙÅ ÎÁÓÔÒÏÊËÉ ÏËÏÎ, ÒÁÓÓÔÁ×ØÔÅ ÉÈ × ÎÕÖÎÙÅ ÐÏÚÉÃÉÉ É ÚÁÎÏ×Ï ××ÅÄÉÔÅ ËÏÍÁÎÄÕ <code>/LAYOUT SAVE</code>. 
+þÔÏÂÙ ÏÂÎÕÌÉÔØ ÎÁÓÔÒÏÊËÉ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/LAYOUT RESET.</code></p>
+
+
+<h3><a id="c5">5. ïËÎÁ status É msgs &amp; ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ</a></h3>
+
+<p>ðÏ ÕÍÏÌÞÁÎÉÀ "ÄÏÐÏÌÎÉÔÅÌØÎÙÅ ÓÏÏÂÝÅÎÉÑ" ×Ù×ÏÄÑÔÓÑ × ÏËÎÏ ÓÔÁÔÕÓÁ. ðÏÄ ÄÏÐÏÌÎÉÔÅÌØÎÙÍÉ ÐÏÄÒÁÚÕÍÅ×ÁÀÔÓÑ ÓÏÏÂÝÅÎÉÑ, ËÏÔÏÒÙÅ ÎÅ ÐÒÉÎÁÄÌÅÖÁÔ ÎÉ Ë ÏÄÎÏÍÕ ËÁÎÁÌÕ ÉÌÉ ÐÒÉ×ÁÔÕ(ÎÁÐÒÉÍÅÒ ctcp-ÚÁÐÒÏÓÙ). 
+îÅËÏÔÏÒÙÈ ÌÀÄÅÊ ÏÎÉ ÒÁÚÄÒÁÖÁÀÔ, ÔÁË ÞÔÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÈ ÓËÒÙÔØ, ÔÏ ××ÅÄÉÔÅ</p>
+
+<pre>
+/SET use_status_window OFF
+</pre>
+
+<p>üÔÏÔ ÐÁÒÁÍÅÔÒ ÚÁÒÁÂÏÔÁÅÔ ÔÏÌØËÏ ÐÏÓÌÅ ÐÅÒÅÚÁÐÕÓËÁ irssi. åÓÌÉ ×Ù ÈÏÔÉÔÅ ÕÄÁÌÉÔØ ÉÈ ÎÅÍÅÄÌÅÎÎÏ, ÔÏ ÐÒÏÓÔÏ ÚÁËÒÏÊÔÅ ÏËÎÏ(<code>/WINDOW CLOSE</code>).</p>
+
+<p>äÒÕÇÏÅ ÏÓÎÏ×ÎÏÅ ÏËÎÏ - ÜÔÏ "ÏËÎÏ ÓÏÏÂÝÅÎÉÊ", ËÕÄÁ ÉÄÕÔ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ÐÒÉ×ÁÔÁ. 
+ðÏ ÕÍÏÌÞÁÎÉÀ ÏÎÏ ÏÔËÌÀÞÅÎÏ É ×ÍÅÓÔÏ ÜÔÏÇÏ ÄÌÑ ËÁÖÄÏÇÏ ÐÒÉ×ÁÔÁ ÓÏÚÄÁÅÔÓÑ ÎÏ×ÏÅ ÏËÎÏ. þÔÏÂÙ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ÐÒÉ×ÁÔÁ ÛÌÉ × ÏÄÎÏ ÏËÎÏ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ:</p>
+
+<pre>
+/SET use_msgs_window ON
+/SET autocreate_query_level DCCMSGS  (ÉÌÉ ÅÓÌÉ ×Ù ÔÁË-ÖÅ ÎÅ ÈÏÔÉÔÅ
+        ÓÏÚÄÁ×ÁÔØ ÎÏ×ÙÅ ÏËÎÁ ÄÌÑ DCC-ÞÁÔÁ ÎÁÐÉÛÉÔÅ NONE)
+</pre>
+
+<p>üÔÏÔ ÐÁÒÁÍÅÔÒ ÔÁË-ÖÅ ÎÅ ÂÕÄÅÔ ÚÁÄÅÊÓÔ×Ï×ÁΠÄÏ ÐÅÒÅÚÁÐÕÓËÁ irssi. þÔÏÂÙ ÐÒÉÍÅÎÉÔØ ÅÇÏ ÎÅÍÅÄÌÅÎÎÏ ××ÅÄÉÔÅ:</p>
+
+<pre>
+/WINDOW NEW HIDE     - ÓÏÚÄÁÔØ ÏËÎÏ
+/WINDOW NAME (msgs)  - ÐÅÒÅÉÍÅÎÏ×ÁÔØ ÅÇÏ × "(msgs)"
+/WINDOW LEVEL MSGS   - ÐÅÒÅÎÁÐÒÁ×ÉÔØ ×ÓÅ ÐÒÉ×ÁÔÎÙÅ ÓÏÏÂÝÅÎÉÑ × ÜÔÏ ÏËÎÏ
+/WINDOW MOVE 1       - ÓÄÅÌÁÔØ ÜÔÏ ÏËÎÏ ÐÅÒ×ÙÍ × ÓÐÉÓËÅ
+</pre>
+
+<p>õÞÔÉÔÅ, ÞÔÏ ÎÉ use_msgs_window, ÎÉ use_status_window ÎÅ ÂÕÄÕÔ ÒÁÂÏÔÁÔØ ÅÓÌÉ ÉÓÐÏÌØÚÏ×ÁÎÁ ËÏÍÁÎÄÁ <code>/LAYOUT SAVE</code>.</p>
+
+<p>ôÅÐÅÒØ ÍÙ ÐÏÄÏÛÌÉ Ë ÕÒÏ×ÎÑÍ ÓÏÏÂÝÅÎÉÊ.. þÔÏ ÜÔÏ? ÷ÓÅ ÓÏÏÂÝÅÎÉÑ, ËÏÔÏÒÙÅ ×Ù×ÏÄÉÔ irssi ÉÍÅÀÔ ÏÄÉΠÉÌÉ ÂÏÌØÛÅ 
+"ÕÒÏ×ÅÎØ ÓÏÏÂÝÅÎÉÊ". ÷ÏÔ ÏÓÎÏ×ÎÙÅ ÕÒÏ×ÎÉ: PUBLIC - ÄÌÑ ÓÏÏÂÝÅÎÉÊ ÎÁ ËÁÎÁÌÁÈ, 
+MSGS - ÄÌÑ ÐÒÉ×ÁÔÎÙÈ ÓÏÏÂÝÅÎÉÊ É CRAP ÄÌÑ ÏÓÔÁÌØÎÙÈ ÓÏÏÂÝÅÎÉÊ, ËÏÔÏÒÙÅ ÎÅÌØÚÑ ËÌÁÓÓÉÆÉÃÉÒÏ×ÁÔØ. ÷Ù ÍÏÖÅÔÅ ÐÏÌÕÞÉÔØ ÐÏÌÎÙÊ ÓÐÉÓÏË ÕÒÏ×ÎÅÊ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ</p>
+
+<pre>
+/HELP levels
+</pre>
+
+<p>ïËÎÕ ÓÔÁÔÕÓÁ ÐÒÉÓ×ÏÅΠÕÒÏ×ÅÎØ <code>ALL -MSGS</code>, ËÏÔÏÒÙÊ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ×ÓÅ ÓÏÏÂÝÅÎÉÑ, 
+ÉÓËÌÀÞÁÑ ÐÒÉ×ÁÔÎÙÅ, ÄÌÑ ËÏÔÏÒÙÈ ÎÅ ÎÁÚÎÁÞÅÎÏ ÄÒÕÇÏÅ ÍÅÓÔÏ ÉÄÕÔ × ÜÔÏ ÏËÎÏ. âÌÁÇÏÄÁÒÑ ÏÐÃÉÉ <code>-MSGS</code> ÏÎÏ ÎÅ ËÏÎÆÌÉËÔÕÅÔ Ó ÏËÎÏÍ ÓÏÏÂÝÅÎÉÊ.</p>
+
+
+<h3><a id="c6">6. ëÁË × irssi ÒÁÂÏÔÁÅÔ ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ</a></h3>
+
+<p>÷ ircII É ÎÅËÏÔÏÒÙÈ ÄÒÕÇÉÈ IRC-ËÌÉÅÎÔÁÈ ÍÎÏÇÏÓÅÒ×ÅÒÎÁÑ ÐÏÄÄÅÒÖËÁ ÒÅÁÌÉÚÏ×ÁÎÁ × ×ÉÄÅ ÐÏÍÅÝÅÎÉÑ ×ËÌÁÄËÉ Ó ÓÅÒ×ÅÒÏÍ × ÓÐÉÓÏË ÏËÏÎ
+. ÷ IRSSI îåô. îÅÔ ÎÉËÁËÏÊ Ó×ÑÚÉ ÍÅÖÄÕ ÏËÎÏÍ É ÓÅÒ×ÅÒÏÍ. ÷Ù ÍÏÖÅÔÅ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÄÅÓÑÔÉ ÓÅÒ×ÅÒÁÍ ÏÄÎÏ×ÒÅÍÅÎÎÏ É ÕÐÒÁ×ÌÑÔØ ÉÍÉ ×ÓÅÍÉ ÉÚ ÏÄÎÏÇÏ ÏËÎÁ, ÉÌÉ ÚÁÈÏÄÉÔØ ÎÁ ËÁÎÁÌÙ ÎÁ ËÁÖÄÏÍ ÉÚ ÎÉÈ 
+× ÏÄÎÏÍ ÏËÎÅ, ÅÓÌÉ ×Ù ÄÅÊÓÔ×ÉÔÅÌØÎÏ ÜÔÏÇÏ ÈÏÔÉÔÅ. ëÁË ÂÙÌÏ ÓËÁÚÁÎÏ ×Ù ÍÏÖÅÔÅ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÎÏ×ÏÍÕ ÓÅÒ×ÅÒÕ, ÎÅ ÚÁËÒÙ×ÁÑ ÔÅËÕÝÅÇÏ ÓÏÅÄÉÎÅÎÉÑ:</p>
+
+<pre>
+/CONNECT irc.server.org
+</pre>
+
+<p>÷ÍÅÓÔÏ ËÏÍÁÎÄÙ <code>/SERVER</code>, ËÏÔÏÒÁÑ ÚÁËÒÙ×ÁÅÔ ÓÕÝÅÓÔ×ÕÀÝÅÅ
+ÓÏÅÄÉÎÅÎÉÅ. þÔÏÂÙ ÐÏÓÍÏÔÒÅÔØ ÓÐÉÓÏË ÏÓÕÝÅÓÔ×ÌÅÎÎÙÈ ÓÏÅÄÉÎÅÎÉÊ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/SERVER</code>
+ÂÅÚ ÐÁÒÁÍÅÔÒÏ×. ÷Ù Õ×ÉÄÉÔÅ ÐÒÉÍÅÒÎÏ ÓÌÅÄÕÀÝÅÅ:</p>
+
+<pre>
+-!- IRCNet: irc.song.fi:6667 (IRCNet)
+-!- OFTC: irc.oftc.net:6667 (OFTC)
+-!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
+</pre>
+
+<p>úÄÅÓØ ×ÉÄÎÏ, ÞÔÏ ÍÙ ÐÏÄËÌÀÞÅÎÙ Ë ÓÅÔÑÍ IRCNet É OFTC.
+îÁÄÐÉÓØ IRCNet × ÎÁÞÁÌÅ Ñ×ÌÑÅÔÓÑ "ÍÅÔËÏÊ ÓÅÒ×ÅÒÁ" Á 
+(IRCnet) × ËÏÎÃÅ ÐÏËÁÚÙ×ÁÅÔ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÕÀ IRC-ÓÅÔØ. íÅÔËÁ ÓÅÒ×ÅÒÁ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÕÎÉËÁÌØÎÏÍÕ ÉÍÅÎÉ, ËÏÔÏÒÏÅ ÏÂÙÞÎÏ ÓÏ×ÐÁÄÁÅÔ Ó ÎÁÚ×ÁÎÉÅÍ ÓÅÔÉ.
+ëÏÇÄÁ IRC-ÓÅÔØ ÎÅ ÉÚ×ÅÓÔÎÁ ÜÔÏ ËÁËÁÑ-ÔÏ ÞÁÓÔØ ÉÍÅÎÉ ÓÅÒ×ÅÒÁ.
+ëÏÇÄÁ ÏÓÕÝÅÓÔ×ÌÅÎÙ ÎÅÓËÏÌØËÏ ÓÏÅÄÉÎÅÎÉÊ Ó ÏÄÎÏÊ ÓÅÔØÀ ÉÌÉ ÓÅÒ×ÅÒÏÍ, irssi
+ÄÏÂÁ×ÌÑÅÔ ÃÉÆÒÕ ÐÏÓÌÅ ÍÅÔËÉ, ÔÁË ÞÔÏ ÜÔÏ ÍÏÖÅÔ ÂÙÔØ ircnet, ircnet2, ircnet3
+É.Ô.Ä.</p>
+
+<p>íÅÔËÁ ÓÅÒ×ÅÒÁ, ÎÁÞÉÎÁÀÝÁÑÓÑ Ó <code>RECON-</code> ÏÂÏÚÎÁÞÁÅÔ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÅ. 
+÷ ×ÙÛÅÐÒÉ×ÅÄÅÎÎÏÍ ÐÒÉÍÅÒÅ ÍÙ ×ÉÄÉÍ, ÞÔÏ ÐÏÄËÌÀÞÅÎÉÅ Ë ÓÅÒ×ÅÒÕ 192.168.0.1 ÂÙÌÏ ÎÅÕÄÁÞÎÙÍ É 
+irssi ÐÏÐÒÏÂÕÅÔ ÐÏÄËÌÀÞÉÔØÓÑ ÚÁÎÏ×Ï ÞÅÒÅÚ 3 ÍÉÎÕÔÙ.</p>
+
+<p>þÔÏÂÙ ÏÔËÌÀÞÉÔØÓÑ ÏÔ ÓÅÒ×ÅÒÁ ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÉÅ ËÏÍÁÎÄÙ:</p>
+
+<pre>
+/DISCONNECT ircnet   - ÏÔËÌÀÞÉÔØÓÑ ÏÔ ÓÅÒ×ÅÒÁ Ó ÍÅÔËÏÊ "ircnet"
+/DISCONNECT recon-1  - ÏÓÔÁÎÏ×ÉÔØ ÐÏÐÙÔËÉ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÕ RECON-1
+/RMRECONNS           - ÏÓÔÁÎÏ×ÉÔØ ×ÓÅ ÐÏÐÙÔËÉ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÑ
+
+/RECONNECT recon-1   - ÎÅÍÅÄÌÅÎÎÏ ÐÏÐÒÏÂÏ×ÁÔØ ÐÅÒÅÐÏÄËÌÀÞÉÔØÓÑ Ë RECON-1
+/RECONNECT ALL       - ÎÅÍÅÄÌÅÎÎÏ ÐÏÐÒÏÂÏ×ÁÔØ ÐÅÒÅÐÏÄËÌÀÞÉÔØÓÑ ËÏ ×ÓÅÍ ÓÅÒ×ÅÒÁÍ
+                                       × ÏÞÅÒÅÄÉ ÎÁ ÐÏÄËÌÀÞÅÎÉÅ
+</pre>
+
+<p>ôÅÐÅÒØ, ËÏÇÄÁ ×Ù ÐÏÄËÌÀÞÅÎÙ ËÏ ×ÓÅÍ ÓÅÒ×ÅÒÁÍ ×Ù ÄÏÌÖÎÙ ÚÎÁÔØ ËÁË ÕËÁÚÁÔØ ËÁËÏÊ ÉÚ ÎÉÈ ×Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ. 
+åÄÉÎÓÔ×ÅÎÎÙÊ ÓÐÏÓÏ - ÜÔÏ ÉÍÅÔØ ÐÕÓÔÏÅ ÏËÎÏ ÎÁÐÏÄÏÂÅ ÏËÎÁ ÓÔÁÔÕÓÁ. ÷ ÎÅÍ ×Ù ÍÏÖÅÔÅ ×ÙÂÒÁÔØ ËÁËÏÊ ÓÅÒ×ÅÒ ÈÏÔÉÔÅ ÓÄÅÌÁÔØ ÁËÔÉ×ÎÙÍ</p>
+
+<pre>
+/WINDOW SERVER tag    - ÓÄÅÌÁÔØ ÓÅÒ×ÅÒ Ó ÍÅÔËÏÊ "tag" ÁËÔÉ×ÎÙÍ
+Ctrl-X                - óÄÅÌÁÔØ ÓÌÅÄÕÀÝÉÊ × ÓÐÉÓËÅ ÓÅÒ×ÅÒ ÁËÔÉ×ÎÙÍ
+</pre>
+
+<p>ëÏÇÄÁ ÓÅÒ×ÅÒ ÁËÔÉ×ÎÙÊ ×Ù ÍÏÖÅÔÅ ÎÏÒÍÁÌØÎÏ ÅÇÏ ÉÓÐÏÌØÚÏ×ÁÔØ. ëÏÇÄÁ ×Ù ÐÏÄËÌÀÞÅÎÙ Ë ÎÅÓËÏÌØËÉÍ ÓÅÒ×ÅÒÁÍ, irssi ÄÏÂÁ×ÌÑÅÔ ÐÒÅÆÉËÓ [ÍÅÔËÁ_ÓÅÒ×ÅÒÁ] 
+ËÏ ×ÓÅÍ ÓÏÏÂÝÅÎÉÑÍ, ÎÅ ÏÔÎÏÓÑÝÉÍÓÑ Ë ËÁÎÁÌÕ ÉÌÉ ÐÒÉ×ÁÔÕ ÔÁË ÞÔÏ ×Ù ÍÏÖÅÔÅ ÚÎÁÔØ Ó ËÁËÏÇÏ ÓÅÒ×ÅÒÁ ÏÎÏ ÐÒÉÛÌÏ.</p>
+
+<p>îÅËÏÔÏÒÙÅ ËÏÍÁÎÄÙ ÔÁË-ÖÅ ÐÏÚ×ÏÌÑÀÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÏÐÃÉÀ <code>-ÍÅÔËÁ_ÓÅÒ×ÅÒÁ</code> 
+ÞÔÏÂÙ ÕËÁÚÁÔØ ÄÌÑ ËÁËÏÇÏ ÓÅÒ×ÅÒÁ ×Ù ÈÏÔÉÔŠţ ÉÓÐÏÌØÚÏ×ÁÔØ:</p>
+
+<pre>
+/MSG -ÍÅÔËÁ ÎÉË ÓÏÏÂÝÅÎÉÅ
+/JOIN -ÍÅÔËÁ #ËÁÎÁÌ
+/QUERY -ÍÅÔËÁ ÎÉË
+</pre>
+
+<p>á×ÔÏÄÏÐÏÌÎÅÎÉÅ ËÏÍÁÎÄÙ <code>/MSG</code> ÔÁË-ÖÅ Á×ÔÏÍÁÔÉÞÅÓËÉ ÄÏÂÁ×ÌÑÅÔ ÍÅÔËÕ ÓÅÒ×ÅÒÁ
+ ËÏÇÄÁ ÎÉË ÎÅ ÎÁ ÁËÔÉ×ÎÏÍ ÓÅÒ×ÅÒÅ.</p>
+
+<p>ïËÎÏ ÓÅÒ×ÅÒÁ ÍÏÖÎÏ ÓÄÅÌÁÔØ ÚÁËÒÅÐÌ£ÎÎÙÍ. ëÏÇÄÁ ÏÎÏ ÚÁËÒÅÐÌÅÎÏ, ÔÏ ÏÎÏ ÎÉËÏÇÄÁ Á×ÔÏÍÁÔÉÞÅÓËÉ ÎÅ ÐÅÒÅËÌÀÞÉÔÓÑ ÎÁ ËÁËÏÅ-ÔÏ ÄÒÕÇÏÅ, É ÅÓÌÉ ÐÒÏÉÚÏÛÌÏ ÏÔËÌÀÞÅÎÉÅ ÏÔ ÓÅÒ×ÅÒÁ, 
+ÔÏ ÏÎÏ ÎÅ ÂÕÄÅÔ ÉÍÅÔØ ÁËÔÉ×ÎÏÇÏ ÓÅÒ×ÅÒÁ. ëÏÇÄÁ Ë ÓÅÒ×ÅÒÕ ÓÎÏ×Á ÐÒÏÉÚ×ÅÄÅÎÏ ÐÏÄËÌÀÞÅÎÉÅ,
+ÔÏ ÏΠÁ×ÔÏÍÁÔÉÞÅÓËÉ ÓÔÁÎÏ×ÉÔÓÑ ÁËÔÉ×ÎÙÍ × ÜÔÏÍ ÏËÎÅ. þÔÏÂÙ ÚÁËÒÅÐÉÔØ ÏËÎÏ ÓÅÒ×ÅÒÁ ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÕÀ ËÏÍÁÎÄÕ:</p>
+
+<pre>
+/WINDOW SERVER -sticky tag
+</pre>
+
+<p>üÔÏ ÐÏÌÅÚÎÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÍÅÔØ ÏÔÄÅÌØÎÙÅ ÏËÎÁ ÓÔÁÔÕÓÁ É ÓÏÏÂÝÅÎÉÊ ÄÌÑ ËÁÖÄÏÇÏ ÓÅÒ×ÅÒÁ. ÷ÏÔ ËÁË ÜÔÏ ÍÏÖÎÏ ÓÄÅÌÁÔØ (ÐÏ×ÔÏÒÉÔÅ ÄÌÑ ËÁÖÄÏÇÏ ÓÅÒ×ÅÒÁ):</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 É ÐÒÏËÒÕÔËÁ ÏËÏÎ</a></h3>
+
+<p>ëÏÍÁÎÄÁ <code>/LASTLOG</code> ÍÏÖÅÔ ÂÙÔØ ÉÓÐÏÌØÚÏ×ÁÎÁ ÄÌÑ ÐÏÉÓËÁ ÔÅËÓÔÁ × ÂÕÆÅÒÅ ÏËÎÁ. ÷ÏÔ ÐÒÏÓÔÅÊÛÉÅ ÐÒÉÍÅÒ٠ţ ÉÓÐÏÌØÚÏ×ÁÎÉÑ:</p>
+
+<pre>
+/LASTLOG ÓÌÏ×Ï    - ×Ù×ÅÓÔÉ ×ÓÅ ÓÔÒÏËÉ, ÓÏÄÅÒÖÁÝÉÅ "ÓÌÏ×Ï"
+/LASTLOG word 10  - ×Ù×ÅÓÔÉ ÐÏÓÌÅÄÎÉÅ 10 ÓÔÒÏË, ÓÏÄÅÒÖÁÝÉÈ "word"
+/LASTLOG -topics  - ×Ù×ÅÓÔÉ ×ÓÅ ÉÚÍÅÎÅÎÉÑ ÔÏÐÉËÁ
+</pre>
+
+<p>åÓÌÉ ÒÅÚÕÌØÔÁÔÏÍ ×Ù×ÏÄÁ ÄÏÌÖÎÙ ÓÔÁÔØ ÂÏÌÅÅ 1000 ÓÔÒÏË, ÔÏ irssi ÐÒÅÄÐÏÌÏÖÉÔ, ÞÔÏ ×Ù ÄÏÐÕÓÔÉÌÉ ÏÛÉÂËÕ É ×Ù×ÅÄÅÔ ÉÈ ÔÏÌØËÏ Ó ÏÐÃÉÅÊ <code>-force</code>. 
+þÔÏÂÙ ÓÏÈÒÁÎÉÔØ ÓÏÄÅÒÖÉÍÏÅ ÂÕÆÅÒÁ ÏËÎÁ × ÆÁÊÌ, ÉÓÐÏÌØÚÕÊÔÅ ÓÌÅÄÕÀÝÕÀ ËÏÍÁÎÄÕ:</p>
+
+<pre>
+/LASTLOG -file ~/irc.log
+</pre>
+
+<p>ðÒÉ ÉÓÐÏÌØÚÏ×ÁÎÉÉ ÏÐÃÉÉ <code>-file</code> ÏÐÃÉÑ <code>-force</code> 
+ÎÅ ÔÒÅÂÕÅÔÓÑ. õ ËÏÍÁÎÄÙ <code>/LASTLOG</code> ÅÓÔØ ÍÎÏÇÏ ÄÒÕÇÉÈ ÏÐÃÉÊ. þÔÏÂÙ ÐÏÌÕÞÉÔØ ÂÏÌÅÅ ÐÏÄÒÏÂÎÕÀ ÓÐÒÁ×ËÕ ÐÏ ÎÅÊ ÉÓÐÏÌØÚÕÊÔÅ <code>/HELP lastlog</code>.</p>
+
+<p>ëÏÇÄÁ ×Ù ÎÁÛÌÉ ÉÎÔÅÒÅÓÏ×Á×ÛÉÅ ×ÁÓ ÓÔÒÏËÉ, ×ÁÍ ÓËÏÒÅÅ ×ÓÅÇÏ ÚÁÈÏÞÅÔÓÑ ÐÏÓÍÏÔÒÅÔØ ÄÒÕÇÉÅ ÐÒÉÌÅÇÁÀÝÉÅ Ë ÎÉÍ ÓÏÏÂÝÅÎÉÑ. ÷ Irssi ÅÓÔØ ËÏÍÁÎÄÁ <code>/SCROLLBACK</code> (ÉÌÉ
+Å£ ÓÉÎÏÎÉÍ - <code>/SB</code>) ÄÌÑ ÐÅÒÅÍÅÝÅÎÉÑ ÐÏ ÂÕÆÅÒÕ ÏËÎÁ.
+ëÏÍÁÎÄÁ <code>/LASTLOG</code> ×Ù×ÏÄÉÔ ÏÒÉÇÉÎÁÌØÎÏÅ ×ÒÅÍÑ ÓÏÏÂÝÅÎÉÑ
+ É ×Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ËÏÍÁÎÄÕ <code>/SB GOTO ÞÞ:ÍÍ</code> ÞÔÏÂÙ "ÐÅÒÅÐÒÙÇÎÕÔØ" Ë ÜÔÏÍÕ ÆÒÁÇÍÅÎÔÕ ÄÉÓËÕÓÓÉÉ.
+ þÔÏÂÙ ÐÅÒÅÍÅÓÔÉÔØÓÑ ÏÂÒÁÔÎÏ ×ÎÉÚ ÉÓÐÏÌØÚÕÊÔÅ ËÏÍÁÎÄÕ <code>/SB
+END</code>.</p>
+
+
+<h3><a id="c8">8. ÷ÅÄÅÎÉÅ ÌÏÇÏ×</a></h3>
+
+<p>Irssi ÍÏÖÅÔ Á×ÔÏÍÁÔÉÞÅÓËÉ ×ÅÓÔÉ ÌÏÇ ×ÓÅÈ ×ÁÖÎÙÈ ÓÏÏÂÝÅÎÉÊ ËÏÇÄÁ ×Ù × Ü×ÅÅ
+(<code>/AWAY ÐÒÉÞÉÎÁ</code>). ËÏÇÄÁ ×Ù ×ÙÛÌÉ ÉÚ Ü×ÅÑ
+(ÅÝ£ ÒÁÚ ××ÅÄÉÔÅ <code>/AWAY</code>), ÎÏ×ÙÅ ÓÏÏÂÝÅÎÉÑ × Ü×ÅÊ-ÌÏÇÅ ×Ù×ÏÄÑÔÓÑ ÎÁ ÜËÒÁÎ.
+÷Ù ÍÏÖÅÔÅ ÎÁÓÔÒÏÉÔØ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ÓÌÅÄÕÀÝÉÈ ËÏÍÁÎÄ:</p>
+
+<pre>
+/SET awaylog_level MSGS HILIGHT     - ÷ÙÂÉÒÁÅÔ ËÁËÏÅ ÓÏÏÂÝÅÎÉÑ ÎÁÄÏ ÚÁÐÉÓÙ×ÁÔØ × ÌÏÇ
+/SET awaylog_file ~/.irssi/away.log - ÷ÙÂÉÒÁÅÔ ÆÁÊÌ ÄÌÑ ÌÏÇÁ
+</pre>
+
+<p>ðÒÏÓÔÅÊÛÉÊ ÓÐÏÓÏ ×ÅÄÅÎÉÑ ÌÏÇÏ× ÐÒÉ ÐÏÍÏÝÉ Irssi - ×ËÌÀÞÅÎÉÅ Á×ÔÏÌÏÇÁ. 
+Irssi ÂÕÄÅÔ ×ÅÓÔÉ ÌÏÇÉ ×ÓÅÈ ÓÏÏÂÝÅÎÉÊ × ÚÁÄÁÎÎÙÊ ËÁÔÁÌÏÇ.
+÷Ù ÍÏÖÅÔÅ ×ËÌÀÞÉÔØ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÙ:</p>
+
+<pre>
+/SET autolog ON
+</pre>
+
+<p>ðÏ ÕÍÏÌÞÁÎÉÀ × ÌÏÇÉ ÚÁÐÉÓÙ×ÁÅÔÓÑ ÐÏÞÔÉ ×Ó£ ËÒÏÍÅ ÕÒÏ×ÎÅÊ CTCPS ÉÌÉ CRAP
+(<code>/WHOIS</code>-ÚÁÐÒÏÓÙ É.Ô.Ä.). ÷Ù ÍÏÖÅÔÅ ÚÁÄÁÔØ ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ, ËÏÔÏÒÙÅ ÎÁÄÏ ÐÉÓÁÔØ × ÌÏÇÉ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÏÊ:</p>
+
+<pre>
+/SET autolog_level ALL -CRAP -CLIENTCRAP -CTCPS (this is the default)
+</pre>
+
+<p>ðÏ ÕÍÏÌÞÁÎÉÀ irssi ÐÉÛÅÔ ÌÏÇ × ~/irclogs/&lt;ÍÅÔËÁ_ÓÅÒ×ÅÒÁ&gt;/&lt;ÃÅÌØ&gt;.log.
+üÔÏ ÎÁÓÔÒÁÉ×ÁÅÔÓÑ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÏÊ:</p>
+
+<pre>
+/SET autolog_path ~/irclogs/$tag/$0.log (×ÁÒÉÁÎÔ "ÐÏ ÕÍÏÌÞÁÎÉÀ")
+</pre>
+
+<p>åÓÌÉ ÚÁÄÁÎÎÙÊ ËÁÔÁÌÏÇ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ, ÔÏ ÏΠÁ×ÔÏÍÁÔÉÞÅÓËÉ ÓÏÚÄÁÅÔÓÑ. ÷ ÐÅÒÅÍÅÎÎÏÊ $0 
+ÓÏÄÅÒÖÉÔÓÑ ÃÅÌØ(ËÁÎÁÌ ÉÌÉ ÎÉË). ÷Ù ÍÏÖÅÔÅ ÎÁÓÔÒÏÉÔØ Irssi ÔÁË, ÞÔÏÂÙ ÏΠÁ×ÔÏÍÁÔÉÞÅÓËÉ ÄÏÂÁ×ÌÑÌ ÄÁÔÕ/×ÒÅÍÑ Ë ÉÍÅÎÉ ÆÁÊÌÁ Ó ÌÏÇÏÍ. 
+÷ÏÒÍÁÔ ÄÁÔÙ - "man strftime" :). ÷ÏÔ ÐÒÉÍÅÒ:</p>
+
+<pre>
+/SET autolog_path ~/irclogs/%Y/$tag/$0.%m-%d.log
+</pre>
+
+<p>þÔÏÂÙ ×ÅÓÔÉ ÌÏÇÉ ÔÏÌØËÏ ÐÏ ËÁËÉÍ-ÔÏ ÏÔÄÅÌØÎÙÍ ËÁÎÁÌÁÍ ÉÌÉ ÎÉËÁÍ ÓÍÏÔÒÉÔÅ <code>/HELP
+log</code></p>
+
+
+<h3><a id="c9">9. éÚÍÅÎÅÎÉÅ ËÌÁ×ÉÁÔÕÒÎÙÈ ÓÏÞÅÔÁÎÉÊ</a></h3>
+
+<p>÷Ù ÍÏÖÅÔÅ ÉÚÍÅÎÉÔØ ÌÀÂÏÅ ËÌÁ×ÉÁÔÕÒÎÏÅ ÓÏÞÅÔÁÎÉÅ, Ï ËÏÔÏÒÏÍ ÔÅÒÍÉÎÁÌ ÄÁ£Ô ÚÎÁÔØ irssi. 
+ôÏ ÅÓÔØ irssi "×ÉÄÉÔ" ÎÅ ×ÓÅ ËÌÁ×ÉÁÔÕÒÎÙÅ ÓÏÞÅÔÁÎÉÑ, ÎÁÐÒÉÍÅÒ ÏΠÎÅ ÂÕÄÅÔ ÒÅÁÇÉÒÏ×ÁÔØ ÎÁ 
+shift-backspace ÅÓÌÉ ×Ù ËÁË-ÔÏ ÎÅ ÏÔÒÅÄÁËÔÉÒÕÅÔÅ ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÅ X-ÒÅÓÕÒÓÙ.</p>
+
+<p>ëÏÍÁÎÄÁ <code>/HELP bind</code> ÄÁ£Ô ÎÁÍÎÏÇÏ ÂÏÌØÛÅ ÉÎÆÏÒÍÁÃÉÉ Ï ËÌÁ×ÉÁÔÕÒÎÙÈ ÓÏÞÅÔÁÎÉÑÈ, ÞÅÍ ÐÒÉ×ÅÄÅÎÏ ÚÄÅÓØ. 
+ïÂÙÞÎÏ ÐÒÏÂÌÅÍÍÏÊ Ñ×ÌÑÅÔÓÑ "ÚÁÂÉ×ÁÎÉÅ" ËÁËÉÈ-ÔÏ ÎÅ ÓÔÁÎÄÁÒÔÎÙÈ ËÌÁ×ÉÛ. 
+ïÎÉ ÎÅÍÎÏÇÏ ÒÁÚÌÉÞÎÙ ÄÌÑ ËÁÖÄÏÇÏ ÔÅÒÍÉÎÁÌÁ, ÔÁË ÞÔÏ ×Ù ÄÏÌÖÎÙ ÂÕÄÅÔÅ ÕÚÎÁÔØ ÞÔÏ ÉÍÅÎÎÏ ÄÁ£Ô ÎÁÖÁÔÉÅ ÜÔÏÊ ËÌÁ×ÉÛÉ. 
+ðÒÏÓÔÅÊÛÉÊ ÐÕÔØ ÕÚÎÁÔØ ÜÔÏ - ×ÙÐÏÌÎÉÔØ × ËÏÎÓÏÌÉ <code>cat</code> É ÐÏÓÍÏÔÒÅÔØ ÞÔÏ ÂÕÄÅÔ ×Ù×ÏÄÉÔÓÑ ÐÒÉ ÎÁÖÁÔÉÉ ÜÔÏÊ ËÌÁ×ÉÛÉ. 
+÷ÏÔ ÐÒÉÍÅÒ ÎÁÖÁÔÉÑ ËÌÁ×ÉÛÉ F1:</p>
+
+<pre>
+[cras@hurina] ~% cat
+^[OP
+</pre>
+
+<p>ôÁË ÞÔÏ × irssi ÞÔÏÂÙ "ÚÁÂÉÔØ" ÞÔÏ-ÔÏ ÎÁ F1 ×Ù ÄÏÌÖÎÙ ÂÕÄÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ËÏÍÁÎÄÕ <code>/BIND ^[OP /ECHO ÎÁÖÁÔÁ ËÌÁ×ÉÛÁ F1</code>. 
+åÓÌÉ ×Ù ÉÓÐÏÌØÚÕÅÔÅ ÒÁÚÎÙÅ ÔÅÒÍÉÎÁÌÙ, ËÏÔÏÒÙÅ ÐÏ ÒÁÚÎÏÍÕ ÒÁÓÐÏÚÎÁÀÔ ÎÁÖÁÔÉÅ ÏÄÎÏÊ É ÔÏÊ-ÖÅ ËÌÁ×ÉÛÉ, ÔÏ ×ÁÍ ÌÕÞÛÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÞÔÏ-ÔÏ ×ÒÏÄÅ ÜÔÏÇÏ:</p>
+
+<pre>
+/BIND ^[OP key F1
+/BIND ^[11~ key F1
+/BIND F1 /ECHO ÎÁÖÁÔÁ ËÌÁ×ÉÛÁ F1.
+</pre>
+
+<h3><a id="c10">10. ðÒÏËÓÉ É ÂÏÕÎÓÅÒÙ</a></h3>
+
+<p>Irssi ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÏÄËÌÀÞÅÎÉÅ Ë IRC-ÓÅÒ×ÅÒÁÍ ÞÅÒÅÚ ÐÒÏËÓÉ. åÓÌÉ ×Ù ×Ó£ ÐÒÁ×ÉÌØÎÏ ÓÄÅÌÁÅÔÅ, ÔÏ ×ÓÅ ÐÏÄËÌÀÞÅÎÉÑ ÂÕÄÕÔ ÏÓÕÝÅÓÔ×ÌÑÔØÓÑ ÞÅÒÅÚ ÎÅÇÏ É ×ÁÍ ÎÅ ÎÁÄÏ ÂÕÄÅÔ ××ÏÄÉÔØ ÎÉËÁËÉÈ ÄÏÐÏÌÎÉÔÅÌØÎÙÈ ËÏÍÁÎÄ.</p>
+
+<p>÷ÏÔ ÐÒÉÍÅÒ: õ ×ÁÓ ÅÓÔØ ÂÏÕÎÓÅÒ, ×ÉÓÑÝÉÊ ÎÁ
+irc.bouncer.org 5000. ÷Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÅÇÏ ÄÌÑ ÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÁÍ irc.dal.net É irc.efnet.org. äÌÑ ÎÁÞÁÌÁ ×Ù ÄÏÌÖÎÙ ÂÕÄÅÔÅ ÎÁÓÔÒÏÉÔØ ÂÏÕÎÓÅÒ:</p>
+
+<pre>
+/SET use_proxy ON                      (×ËÌÀÞÉÔØ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÐÒÏËÓÉ)
+/SET proxy_address irc.bouncer.org
+/SET proxy_port 5000
+
+/SET proxy_password ÷áû_ðáòïìø
+/SET -clear proxy_string
+/SET proxy_string_after conn %s %d
+</pre>
+
+<p>ðÏÔÏÍ ×ÁÍ ÎÕÖÎÏ ÂÕÄÅÔ ÄÏÂÁ×ÉÔØ ÎÕÖÎÙÅ ÓÅÒ×ÅÒÙ. üÔÏ ÄÅÌÁÅÔÓÑ ÔÏÞÎÏ ÔÁË-ÖÅ, ËÁË ÅÓÌÉ ÂÙ ×Ù ÈÏÔÅÌÉ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÎÉÍ ÎÁÐÒÑÍÕÀ:</p>
+
+<pre>
+/SERVER ADD -auto -ircnet dalnet irc.dal.net
+/SERVER ADD -auto -ircnet efnet irc.efnet.org
+</pre>
+
+<p>ðÏÓÌÅ ÔÏÇÏ, ËÁË ×Ù ÓÄÅÌÁÌÉ ×ÙÛÅÐÅÒÅÞÉÓÌÅÎÎÙÅ ÎÁÓÔÒÏÊËÉ ×ÓÅ ÓÏÅÄÉÎÅÎÉÑ irssi ÂÕÄÅÔ ÐÒÏÉÚ×ÏÄÉÔØ ÞÅÒÅÚ ÐÒÏËÓÀ.</p>
+
+<p>åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÒÏËÓÀ ÄÌÑ ËÁËÏÇÏ-ÔÏ ÓÅÒ×ÅÒÁ, ÔÏ ÐÒÉ ÅÇÏ ÄÏÂÁ×ÌÅÎÉÉ ÕËÁÖÉÔÅ ÏÐÃÉÀ 
+<code>-noproxy</code>.</p>
+
+<p><strong>óÐÅÃÉÆÉÞÎÙÅ ÎÁÓÔÒÏÊËÉ ÄÌÑ ÒÁÚÎÙÈ ÔÉÐÏ× ÐÒÏËÓÉ:</strong></p>
+
+<p>ïÂÙÞÎÙÅ ÎÁÓÔÒÏÊËÉ:</p>
+
+<pre>
+/SET use_proxy ON
+/SET proxy_address &lt;áÄÒÅÓ ÐÒÏËÓÉ&gt;
+/SET proxy_port &lt;ðÏÒÔ&gt;
+</pre>
+
+<p><strong>HTTP proxy</strong></p>
+
+<p>éÓÐÏÌØÚÕÊÔÅ ÜÔÉ ÎÁÓÔÒÏÊËÉ ÄÌÑ HTTP-ÐÒÏËÓÉ:</p>
+
+<pre>
+/SET -clear proxy_password
+/EVAL SET proxy_string CONNECT %s:%d\n\n
+</pre>
+
+<p><strong>BNC</strong></p>
+
+<pre>
+/SET proxy_password ×ÁÛ_ÐÁÒÏÌØ
+/SET -clear proxy_string
+/SET proxy_string_after conn %s %d
+</pre>
+
+<p><strong>dircproxy</strong></p>
+
+<p>dircproxy ÐÒÏÉÚ×ÏÄÉÔ ÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÁÍ ÐÏ ÐÁÒÏÌÑÍ. ôÁË ÞÔÏ ÅÓÌÉ ÎÁÐÒÉÍÅÒ ×Ù ÈÏÔÉÔÅ ÐÏÄËÌÀÞÉÔØÓÑ Ë ÓÅÒ×ÅÒÕ ircnet Ó ÐÁÒÏÌÅÍ ircpass 
+É Ë OFTC Ó ÐÁÒÏÌÅÍ oftcpass, ×Ù ÄÏÌÖÎÙ ÓÄÅÌÁÔØ ÐÒÉÍÅÒÎÏ ÓÌÅÄÕÀÝÅÅ:</p>
+
+<pre>
+/SET -clear proxy_password
+/SET -clear proxy_string
+
+/SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
+/SERVER ADD -auto -ircnet OFTC fake.oftc 6667 oftcpass
+</pre>
+
+<p>éÍÑ ÓÅÒ×ÅÒÁ É ÐÏÒÔ, ËÏÔÏÒÙÅ ×Ù ××ÏÄÉÔÅ ÎÉÇÄÅ ÎÅ ÉÓÐÏÌØÚÕÀÔÓÑ, ÔÁË ÞÔÏ ×Ù ÍÏÖÅÔÅ ÐÉÓÁÔØ ÓÀÄÁ ×Ó£ ÞÔÏ ÕÇÏÄÎÏ.</p>
+
+<p><strong>psyBNC</strong></p>
+
+<p>psyBNC ÉÍÅÅÔ ×ÎÕÔÒÅÎÎÀÀ ÍÎÏÇÏÓÅÒ×ÅÒÎÕÀ ÐÏÄÄÅÒÖËÕ. 
+üÔÏ ÍÏÖÅÔ ÄÏÓÔÁ×ÌÑÔØ ÎÅÂÏÌØÛÉÅ ÎÅÕÄÏÂÓÔ×Á É ÎÅËÏÔÏÒÙÅ ÌÀÄÉ ÐÒÏÓÔÏ ÉÓÐÏÌØÚÕÀÔ ÒÁÚÎÙÅ ÌÏÇÉÎÙ ÄÌÑ ÐÏÄËÌÀÞÅÎÉÑ Ë ÎÅÓËÏÌØËÉÍ ÓÅÒ×ÅÒÁÍ.
+÷Ù ÏÞÅÎØ ÐÒÏÓÔÏ ÍÏÖÅÔÅ ÄÅÌÁÔØ ÜÔÏ ÓÒÅÄÓÔ×ÁÍÉ Irssi:</p>
+
+<pre>
+/SET -clear proxy_password
+/SET -clear proxy_string
+
+/IRCNET ADD -user ircnetuser ircnet
+/SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
+/IRCNET ADD -user oftcuser OFTC
+/SERVER ADD -auto -ircnet OFTC fake.oftc 6667 oftcpass
+</pre>
+
+<p>úÄÅÓØ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ <code>/IRCNET ADD</code> ×Ù ÚÁÄÁÅÔÅ ÉÍÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÅÊ
+É ÐÁÒÏÌÉ ÐÒÉ ÐÏÍÏÝÉ <code>/SERVER ADD</code>.</p>
+
+<p><strong>Irssi proxy</strong></p>
+
+<p>Irssi ×ËÌÀÞÁÅÔ Ó×ÏÀ ÓÏÂÓÔ×ÅÎÎÕÀ ÐÒÏËÓÀ, ËÏÔÏÒÕÀ ×Ù ÍÏÖÅÔÅ ÓÏÂÒÁÔØ ÐÒÉ ÐÏÍÏÝÉ ÏÐÃÉÉ configure 
+<code>--with-proxy</code>. þÔÏÂ٠ţ ÉÓÐÏÌØÚÏ×ÁÔØ ×Ù ÄÏÌÖÎÙ ÏÓÔÁ×ÌÑÔØ irssi ÚÁÐÕÝÅÎÎÙÍ.</p>
+
+<p>Irssi-ÐÒÏËÓÑ ÎÅÍÎÏÇÏ ÏÔÌÉÞÁÅÔÓÑ ÏÔ ÏÓÔÁÌØÎÙÈ ÐÒÏËÓÉ-ÓÅÒ×ÅÒÏ×, ÎÏÒÍÁÌØÎÙÅ ÐÒÏËÓÉ ÓÏÚÄÁÀÔ ÎÏ×ÙÅ ÓÏÅÄÉÎÅÎÉÑ Ó IRC-ÓÅÒ×ÅÒÏÍ ËÏÇÄÁ ×Ù ÈÏÔÉÔÅ Ë ÎÅÍÕ ÐÏÄËÌÀÞÉÔØÓÑ, Á
+<strong>irssi-ÐÒÏËÓÑ ÉÓÐÏÌØÚÕÅÔ ÕÖÅ ÓÕÝÅÓÔ×ÕÀÝÅÅ ÓÏÅÄÉÎÅÎÉÅ(Ñ) ÄÌÑ ×ÓÅÈ ËÌÉÅÎÔÏ×</strong>. éÌÉ ÅÝ£ ÐÏÎÑÔÎÅÅ: <strong>÷Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÔÏÌØËÏ ÏÄÎÏ ÓÏÅÄÉÎÅÎÉÅ Ó IRC-ÓÅÒ×ÅÒÏÍ ÄÌÑ ÎÅÏÇÒÁÎÉÞÅÎÎÏÇÏ ÞÉÓÌÁ ËÌÉÅÎÔÏ×</strong>.</p>
+
+<p>Irssi-ÐÒÏËÓÑ ÍÏÖÅÔ ÒÁÓÐÒÅÄÅÌÑÔØ ÎÅÓËÏÌØËÏ ÓÏÅÄÉÎÅÎÉÊ Ó ÓÅÒ×ÅÒÁÍÉ ÎÁ ÒÁÚÎÙÅ ÐÏÒÔÙ, ÎÁÐÒÉÍÅÒ ÎÁ 2777-ÏÍ ÐÏÒÔÕ Õ ×ÁÓ ÍÏÖÅÔ ÂÙÔØ ÓÏÅÄÉÎÅÎÉÅ Ó ircnet, Á ÎÁ 2778 Ó efnet.</p>
+
+<p>éÓÐÏÌØÚÏ×ÁÎÉÅ ÎÁ ÓÔÏÒÏÎÅ ÐÒÏËÓÉ:</p>
+
+<pre>
+/LOAD proxy
+/SET irssiproxy_password &lt;ÐÁÒÏÌØ&gt;
+/SET irssiproxy_ports &lt;IRC_ÓÅÔØ&gt;=&lt;ÐÏÒÔ&gt; ... (ÎÁÐÒÉÍÅÒ ircnet=2777 efnet=2778)
+</pre>
+<p>÷Ù <strong>ÄÏÌÖÎÙ</strong> ÄÏÂÁ×ÉÔØ ×ÓÅ ÓÅÒ×ÅÒÙ, ËÏÔÏÒÙÅ ×Ù ÉÓÐÏÌØÚÕÅÔÅ × ÓÐÉÓËÉ ÓÅÒ×ÅÒÏ× É ÓÅÔÅÊ 
+ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄ <code>/SERVER ADD</code> É 
+<code>/IRCNET ADD</code>. ..ÒÁÚ×Å ÞÔÏ ÅÓÌÉ ×Ù ÈÏÔÉÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÔÏÌØËÏ ÏÄÎÏ ÓÏÅÄÉÎÅÎÉÅ, ÔÏ ×Ù ÍÏÖÅÔÅ ÕËÁÚÁÔØ:</p>
+
+<pre>
+/SET irssiproxy_ports *=2777
+</pre>
+
+<p>éÓÐÏÌØÚÏ×ÁÎÉÅ ÎÁ ÓÔÏÒÏÎÅ ËÌÉÅÎÔÁ:</p>
+
+<p>ðÒÏÓÔÏ ÐÏÄËÌÀÞÉÔÅÓØ Ë ÐÒÏËÓÅ ËÁË Ë ÎÏÒÍÁÌØÎÏÍÕ ÓÅÒ×ÅÒÕ Ó ÐÁÒÏÌÅÍ, ÚÁÄÁÎÎÙÍ ËÏÍÁÎÄÏÊ <code>/SET irssiproxy_password</code>. ðÒÉÍÅÒ:</p>
+
+<pre>
+/SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
+/SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
+</pre>
+
+<p>Irssi-ÐÒÏËÓÑ ÔÁË-ÖÅ ÎÏÒÍÁÌØÎÏ ÒÁÂÏÔÁÅÔ Ó ÄÒÕÇÉÍÉ irc-ËÌÉÅÎÔÁÍÉ.</p>
+
+<p><strong>SOCKS</strong></p>
+
+Irssi ÍÏÖÅÔ ÂÙÔØ ÓÏÂÒÁΠӠÐÏÄÄÅÒÖËÏÊ socks-ÐÒÏËÓÉ (ÏÐÃÉÑ configure <code>--with-socks</code>), 
+ÎÏ Ñ ÎÁ ÓÁÍÏÍ ÄÅÌÅ ÎÅ ÚÎÁÀ ËÁË ÏÎÏ ÒÁÂÏÔÁÅÔ. îÁÓÔÒÏÊËÉ <code>/SET
+proxy</code> ÎÁ ÜÔÉ ÐÒÏËÓÉ ÎÉËÁË ÎÅ ÄÅÊÓÔ×ÕÀÔ.
+
+<p><strong>äÒÕÇÉÅ ÐÒÏËÓÉ</strong></p>
+
+<p>IRC-ÂÏÕÎÓÅÒÙ ÏÂÙÞÎÏ ÒÁÂÏÔÁÀÔ ÔÏÞÎÏ ÔÁË-ÖÅ ËÁË É IRC-ÓÅÒ×ÅÒÙ, ÎÏ ÐÒÏÓÑÔ ÐÁÒÏÌØ. ÷Ù ÍÏÖÅÔÅ ÄÁÔØ ÉÍ ÅÇÏ ÐÒÉ ÐÏÍÏÝÉ ÓÌÅÄÕÀÝÅÊ ËÏÍÁÎÄÙ:</p>
+
+<pre>
+/SET proxy_password &lt;ÐÁÒÏÌØ&gt;
+</pre>
+
+<p>CONNECT-ÓÔÒÏËÉ ÐÏ ÕÍÏÌÞÁÎÉÀ:</p>
+
+<pre>
+/SET proxy_string CONNECT %s %d
+/SET proxy_string_after
+</pre>
+
+<p>proxy_string ÏÔÐÒÁ×ÌÑÀÔÓÑ ÐÅÒÅÄ ËÏÍÁÎÄÁÍÉ NICK/USER, Á 
+proxy_string_after ÏÔÐÒÁ×ÌÑÅÔÓÑ ÐÏÓÌÅ ÎÉÈ. %s and %d can be used with both
+of them.</p>
+
+<h3><a id="c11">11. îÁÓÔÒÏÊËÉ Irssi</a></h3>
+
+<p>÷ÁÍ ÍÏÇÕÔ ÎÅ ÐÏÎÒÁ×ÉÔÓÑ ÎÁÓÔÒÏÊËÉ Irssi ÐÏ ÕÍÏÌÞÁÎÉÀ.
+÷ÏÔ ÎÅËÏÔÏÒÙÅ ÉÚ ÎÉÈ, ËÏÔÏÒÙÅ ×Ù ÓËÏÒÅÅ ×ÓÅÇÏ ÚÁÈÏÔÉÔÅ ÉÚÍÅÎÉÔØ(× ÐÒÉÍÅÒÁÈ ÐÒÉ×ÅÄÅÎÙ "ÕÍÏÌÞÁÌØÎÙÅ" ÚÎÁÞÅÎÉÑ):</p>
+
+<p><strong>ïËÎÁ ÐÒÉ×ÁÔÏ×</strong></p>
+
+<dl>
+<dt>/SET autocreate_own_query ON</dt>
+  <dd>á×ÔÏÍÁÔÉÞÅÓËÉ ÓÏÚÄÁ×ÁÔØ ÏËÎÏ ÐÒÉ×ÁÔÁ ËÏÇÄÁ ×Ù ÏÔÐÒÁ×ÌÑÅÔÅ ËÏÍÕ-ÔÏ ÓÏÏÂÝÅÎÉÅ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ <code>/MSG</code>.</dd>
+
+<dt>/SET autocreate_query_level MSGS</dt>
+  <dd>ðÒÉ ÐÏÌÕÞÅÎÉÉ ÓÏÏÂÝÅÎÉÊ ÏËÎÏ ÐÒÉ×ÁÔÁ ÓÏÚÄÁÅÔÓÑ Ó ÜÔÉÍ ÕÒÏ×ÎÅÍ ÓÏÏÂÝÅÎÉÊ. óÅÊÞÁÓ ÒÁÂÏÔÁÀÔ ÔÏÌØËÏ MSGS, DCCMSGS É NOTICES.
+   ÷Ù ÍÏÖÅÔÅ ÏÔÍÅÎÉÔØ ÜÔÏ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ <code>/SET -clear autocreate_query_level</code>.</dd>
+
+<dt>/SET autoclose_query 0</dt>
+  <dd>ïËÎÁ ÐÒÉ×ÁÔÏ× ÍÏÇÕÔ ÂÙÔØ Á×ÔÏÍÁÔÉÞÅÓËÉ ÚÁËÒÙÔÙ ÐÏÓÌÅ ÚÁÄÁÎÎÏÇÏ "ÐÒÏÓÔÏÑ". áËÔÉ×ÎÏÅ ÏËÎÏ É ÏËÎÁ Ó ÎÅÐÒÏÞÉÔÁÎÎÙÍÉ ÓÏÏÂÝÅÎÉÑÍÉ ÎÅ ÚÁËÒÙ×ÁÀÔÓÑ. úÎÁÞÅÎÉÅ ÚÁÄÁÅÔÓÑ × ÓÅËÕÎÄÁÈ.</dd>
+</dl>
+
+<p><strong>ïËÎÁ</strong></p>
+
+<dl>
+<dt>/SET use_msgs_window OFF</dt>
+  <dd>óÏÚÄÁ×ÁÔØ ÏËÎÏ ÓÏÏÂÝÅÎÉÊ ÐÒÉ ÚÁÐÕÓËÅ. ÷ÓÅ ÐÒÉ×ÁÔÎÙÅ ÓÏÏÂÝÅÎÉÑ ÂÕÄÕÔ ÎÁÐÒÁ×ÌÑÔØÓÑ × ÜÔÏ ÏËÎÏ. 
+  üÔÏ ÉÍÅÅÔ ÓÍÙÓÌ ÔÏÌØËÏ ÅÓÌÉ ×Ù ÏÔÍÅÎÉÌÉ Á×ÔÏÓÏÚÄÁÎÉÅ ÏËÏΠÐÒÉ×ÁÔÏ×. 
+  üÔÏ ÏËÎÏ ÔÁË-ÖÅ ÍÏÖÅÔ ÂÙÔØ ÓÏÚÄÁÎÏ ×ÒÕÞÎÕÀ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄÙ /WINDOW LEVEL
+  MSGS, /WINDOW NAME (msgs).</dd>
+
+<dt>/SET use_status_window ON</dt>
+  <dd>óÏÚÄÁ×ÁÔØ ÏËÎÏ ÓÔÁÔÕÓÁ ÐÒÉ ÚÁÐÕÓËÅ. ÷ÓÅ ÓÏÏÂÝÅÎÉÑ, ËÏÔÏÒÙÅ ÂÏÌØÛÅ ÎÅËÕÄÁ ÏÔÐÒÁ×ÉÔØ ÉÄÕÔ ÓÀÄÁ, ×ËÌÀÞÁÑ /WHOIS É.Ô.Ä. 
+  ïËÎÏ ÓÔÁÔÕÓÁ ÔÏÖÅ ÍÏÖÅÔ ÂÙÔØ ÓÏÚÄÁÎÏ ×ÒÕÞÎÕÀ ÐÒÉ ÐÏÍÏÝÉ ËÏÍÁÎÄ <code>/WINDOW LEVEL ALL -MSGS</code>,
+  <code>/WINDOW NAME (status)</code>.</dd>
+
+<dt>/SET autocreate_windows ON</dt>
+  <dd>åÓÌÉ ×Ù ÜÔÏ ÏÔËÌÀÞÉÔÅ, ÔÏ ×ÓÅ ÓÏÏÂÝÅÎÉÑ ÂÕÄÕÔ ÐÏÍÅÝÁÔØÓÑ × ÏÄÎÏ ÏËÎÏ</dd>
+
+<dt>/SET autoclose_windows ON</dt>
+  <dd>á×ÔÏÚÁËÒÙÔÉÅ ÏËÏΠ(ÎÁÐÒÉÍÅÒ ÐÒÉ ×ÙÈÏÄÅ Ó ËÁÎÁÌÏ×(<code>/PART</code>)).</dd>
+
+<dt>/SET reuse_unused_windows OFF</dt>
+  <dd>ëÏÇÄÁ ÉÝÅÔÓÑ ÍÅÓÔÏ ÄÌÑ ÓÏÚÄÁÎÉÑ ÎÏ×ÏÇÏ ÏËÎÁ (ËÁÎÁÌÁ ÉÌÉ ÐÒÉ×ÁÔÁ) Irssi 
+  ÓÎÁÞÁÌÁ ÐÙÔÁÅÔÓÑ ÉÓÐÏÌØÚÏ×ÁÔØ ÕÖÅ ÓÕÝÅÓÔ×ÕÀÝÉÅ ÐÕÓÔÙÅ ÏËÎÁ. åÓÌÉ ÜÔÁ ÏÐÃÉÑ ×ËÌÀÞÅÎÁ, ÔÏ ×ÓÅÇÄÁ ÂÕÄÕÔ ÓÏÚÄÁ×ÁÔØÓÑ ÎÏ×ÙÅ ÏËÎÁ. 
+  üÔÁ ÎÁÓÔÒÏÊËÁ ÉÇÎÏÒÉÒÕÅÔÓÑ ÅÓÌÉ autoclose_windows ×ËÌÀÞÅÎ.</dd>
+
+<dt>/SET window_auto_change OFF</dt>
+  <dd>á×ÔÏÍÁÔÉÞÅÓËÉ ÐÅÒÅËÌÀÞÁÔØÓÑ × Á×ÔÏÍÁÔÉÞÅÓËÉ ÓÏÚÄÁÎÎÙÅ ÏËÎÁ.</dd>
+
+<dt>/SET print_active_channel OFF</dt>
+  <dd>ëÏÇÄÁ ×Ù ÄÅÒÖÉÔÅ × ÏÄÎÏÍ ÏËÎÅ ÂÏÌØÛÅ ÞÅÍ ÏÄÉΠËÁÎÁÌ, Irssi ×Ù×ÏÄÉÔ ÓÏÏÂÝÅÎÉÑ, ÐÒÉÈÏÄÑÝÉÅ ÎÁ ÁËÔÉ×ÎÙÊ ËÁÎÁÌ × ÆÏÒÍÅ <code>&lt;ÎÉË&gt; ÔÅËÓÔ</code>
+  Á ÔÅ, ÞÔÏ ÐÒÉÈÏÄÑÔ ÎÁ ÄÒÕÇÉÅ ËÁÎÁÌÙ ÔÁË: <code>&lt;ÎÉË:ËÁÎÁÌ&gt; ÔÅËÓÔ</code>. åÓÌÉ ÜÔÁ ÏÐÃÉÑ ×ËÌÀÞÅÎÁ, ÔÏ ÓÏÏÂÝÅÎÉÑ, ÐÒÉÈÏÄÑÝÉÅ ÎÁ ÁËÔÉ×ÎÙÊ ËÁÎÁÌ ÂÕÄÕÔ ÔÁË-ÖÅ ×Ù×ÏÄÉÔØÓÑ ×Ï ×ÔÏÒÏÍ ×ÁÒÉÁÎÔÅ.</dd>
+
+<dt>/SET window_history OFF</dt>
+  <dd>èÒÁÎÉÔØ ÏÔÄÅÌØÎÕÀ ÉÓÔÏÒÉÀ ËÏÍÁÎÄ ÄÌÑ ËÁÖÄÏÇÏ ÏËÎÁ.</dd>
+</dl>
+
+
+<p><strong>éÎÆÏÒÍÁÃÉÑ Ï ÐÏÌØÚÏ×ÁÔÅÌÅ</strong></p>
+
+<dl>
+<dt>/SET nick</dt>
+  <dd>÷ÁÛ ÎÉË</dd>
+
+<dt>/SET alternate_nick</dt>
+  <dd>÷ÁÛ ÁÌØÔÅÒÎÁÔÉ×ÎÙÊ ÎÉË.</dd>
+
+<dt>/SET user_name</dt>
+  <dd>÷ÁÛÅ ÉÍÑ ÐÏÌØÚÏ×ÁÔÅÌÑ. åÓÌÉ Õ ×ÁÓ ×ËÌÀÞÅΠident, ÔÏ ÏÎÏ ÎÉÞÅÇÏ ÎÅ ÄÁ£Ô.</dd>
+
+<dt>/SET real_name</dt>
+  <dd>÷ÁÛÅ ÎÁÓÔÏÑÝÅÅ ÉÍÑ.</dd>
+</dl>
+
+
+<p><strong>éÎÆÏÒÍÁÃÉÑ Ï ÓÅÒ×ÅÒÅ</strong></p>
+
+<dl>
+<dt>/SET skip_motd OFF</dt>
+  <dd>ðÒÏÐÕÓËÁÔØ motd ÐÒÉ ÐÏÄËÌÀÞÅÎÉÉ Ë ÓÅÒ×ÅÒÕ.</dd>
+
+<dt>/SET server_reconnect_time 300</dt>
+  <dd>óËÏÌØËÏ ÓÅËÕÎÄ ÎÁÄÏ ÖÄÁÔØ ÐÅÒÅÄ ÐÏ×ÔÏÒÎÏÊ ÐÏÐÙÔËÏÊ ÐÏÄËÌÀÞÅÎÉÑ Ë ÓÅÒ×ÅÒÕ.</dd>
+
+<dt>/SET lag_max_before_disconnect 300</dt>
+  <dd>ðÒÉ ËÁËÏÍ ÌÁÇÅ(× ÓÅËÕÎÄÁÈ) ÎÁÄÏ ÏÔËÌÀÞÁÔØÓÑ ÏÔ ÓÅÒ×ÅÒÁ É ÐÒÅÄÐÒÉÎÉÍÁÔØ ÐÏÐÙÔËÕ ÐÅÒÅÐÏÄËÌÀÞÅÎÉÑ.</dd>
+</dl>
+
+
+<p><strong>÷ÎÅÛÎÉÊ ×ÉÄ</strong></p>
+
+<dl>
+<dt>/SET timestamps ON</dt>
+  <dd>ðÏËÁÚÙ×ÁÔØ ×ÒÅÍÑ ÐÅÒÅÄ ËÁÖÄÙÍ ÓÏÏÂÝÅÎÉÅÍ.</dd>
+
+<dt>/SET hide_text_style OFF</dt>
+  <dd>óËÒÙÔØ ÏÆÏÒÍÌÅÎÉÅ ÔÅËÓÔÁ(ÖÉÒÎÙÊ ÛÒÉÆÔ, Ã×ÅÔÁ É.Ô.Ä.).</dd>
+
+<dt>/SET show_nickmode ON</dt>
+  <dd>ðÏËÁÚÙ×ÁÔØ "ÒÅÖÉÍ ÎÉËÁ" ÎÁ ËÁÎÁÌÁÈ, ÎÁÐÒÉÍÅÒ
+  <code>&lt;@nick&gt;</code> Õ ÏÐÏ×, <code>&lt;+nick&gt;</code> Õ ×ÏÊÓÏ× É.Ô.Ä.</dd>
+
+<dt>/SET show_nickmode_empty ON</dt>
+  <dd>åÓÌÉ Õ ÎÉËÁ ÎÅÔ ÒÅÖÉÍÁ - ×Ù×ÏÄÉÔØ ÐÒÏÂÅÌ ÎÁ ÍÅÓÔÅ "ÓÉÍ×ÏÌÁ ÒÅÖÉÍÁ".</dd>
+
+<dt>/SET show_quit_once OFF</dt>
+  <dd>ðÏËÁÚÙ×ÁÔØ quit-ÓÏÏÂÝÅÎÉÅ ÔÏÌØËÏ × ÏÄÎÏÍ ÏËÎÅ, ÅÓÌÉ ÞÅÌÏ×ÅË ×ÙÛÅÌ Ó ÎÅÓËÏÌØËÉÈ ËÁÎÁÌÏ×, ÎÁ ËÏÔÏÒÙÈ ×Ù ÓÉÄÉÔÅ.</dd>
+
+<dt>/SET lag_min_show 100</dt>
+  <dd>ðÏËÁÚÙ×ÁÔØ × ÓÔÁÔÕÓ-ÂÁÒÅ ÌÁÇ ÅÓÌÉ ÏΠÐÒÅ×ÙÛÁÅÔ ÚÁÄÁÎÎÏÅ ÞÉÓÌÏ ÀÎÉÔÏ×. ÷ ÏÄÎÏÊ ÓÅËÕÎÄÅ 100 ÀÎÉÔÏ×.</dd>
+
+<dt>/SET indent 10</dt>
+  <dd>åÓÌÉ ÓÔÒÏËÁ, ËÏÔÏÒÕÀ ÎÁÄÏ ×Ù×ÅÓÔÉ ÎÅ ×ÍÅÝÁÅÔÓÑ × ÏÄÎÕ ÓÔÒÏËÕ, ÔÏ ÏÎÁ ÒÁÚÂÉ×ÁÅÔÓÑ É ×Ù×ÏÄÉÔÓÑ ÎÁ ÓÌÅÄÕÀÝÉÈ ÓÔÒÏËÁÈ. üÔÏÔ ÐÁÒÁÍÅÔÒ ÐÏËÁÚÙ×ÁÅÔ ÓËÏÌØËÏ ÍÅÓÔÁ ÎÁÄÏ ÏÔÓÔÕÐÉÔØ ÐÅÒÅÄ ÎÁÞÁÌÏÍ ×Ù×ÏÄÁ ÔÅËÓÔÁ ÎÁ ÓÌÅÄÕÀÝÉÈ ÓÔÒÏËÁÈ. 
+  üÔÏ ÍÏÖÅÔ ÂÙÔØ ÐÅÒÅÏÐÒÅÄÅÌÅÎÏ × ÎÁÓÔÒÏÊËÁÈ ÆÏÒÍÁÔÉÒÏ×ÁÎÉÑ ÔÅËÓÔÁ ÐÒÉ ÐÏÍÏÝÉ ÆÏÒÍÁÔÁ <code>%|</code>.</dd>
+
+<dt>/SET activity_hide_targets</dt>
+  <dd>åÓÌÉ ×Ù ÎÅ ÈÏÔÉÔÅ ×ÉÄÅÔØ ÁËÔÉ×ÎÏÓÔØ ÎÁ ËÁËÉÈ-ÔÏ ËÁÎÁÌÁÈ ÉÌÉ ÐÒÉ×ÁÔÁÈ, ÔÏ ÐÅÒÅÞÉÓÌÉÔÅ ÉÈ ÚÄÅÓØ. îÁÐÒÉÍÅÒ <code>#boringchannel =bot1
+  =bot2</code>. üÔÁ ÎÁÓÔÒÏÊËÁ ÉÇÎÏÒÉÒÕÅÔÓÑ ÅÓÌÉ ×ÓÔÒÅÞÁÅÔÓÑ ÔÅËÓÔ ÉÌÉ ÓÏÏÂÝÅÎÉÅ, ÄÌÑ ËÏÔÏÒÏÇÏ ×Ù ÎÁÓÔÒÏÉÌÉ ÐÏÄÓ×ÅÔËÕ(highlight).</dd>
+</dl>
+
+<p><strong>á×ÔÏÄÏÐÏÌÎÅÎÉÅ ÎÉËÏ×</strong></p>
+
+<dl>
+<dt>/SET completion_auto OFF</dt>
+  <dd>á×ÔÏÍÁÔÉÞÅÓËÉ ÄÏÐÏÌÎÑÔØ ÎÉË ÅÓÌÉ ÓÔÒÏËÁ ÎÁÞÉÎÁÅÔÓÑ Ó ÐÅÒ×ÙÈ ÂÕË× ÎÉËÁ É "ÓÉÍ×ÏÌÁ Á×ÔÏÄÏÐÏÌÎÅÎÉÑ". 
+  ìÕÞÛÅ ×ÓÅÇÏ ÉÓÐÏÌØÚÏ×ÁÔØ Á×ÔÏÄÏÐÏÌÎÅÎÉÅ ÔÁÂÏÍ.</dd>
+
+<dt>/SET completion_char :</dt>
+  <dd>"óÉÍ×ÏÌ Á×ÔÏÄÏÐÏÌÎÅÎÉÑ".</dd>
+</dl>
+
+<h3><a id="c12">12. ðÁÎÅÌØ ÓÔÁÔÕÓÁ</a></h3>
+
+<p>ëÏÍÁÎÄÁ <code>/STATUSBAR</code> ×Ù×ÏÄÉÔ ÓÐÉÓÏË ÐÁÎÅÌÅÊ ÓÔÁÔÕÓÁ:</p>
+
+<pre>
+Name                           Type   Placement Position Visible
+window                         window bottom    0        always
+window_inact                   window bottom    1        inactive
+prompt                         root   bottom    100      always
+topic                          root   top       1        always
+</pre>
+
+<p><code>/STATUSBAR &lt;ÉÍÑ&gt;</code> ×Ù×ÏÄÉÔ ÎÁÓÔÒÏÊËÉ ÐÁÎÅÌÉ ÓÔÁÔÕÓÁ É Å£ ËÏÍÐÏÎÅÎÔÙ. 
+<code>/STATUSBAR &lt;ÉÍÑ&gt; ENABLE|DISABLE</code>
+×ËÌÀÞÁÅÔ ÉÌÉ ÏÔËÌÀÞÁÅÔ ÐÁÎÅÌØ. <code>/STATUSBAR &lt;ÉÍÑ&gt; RESET</code>
+ÕÓÔÁÎÁ×ÌÉ×ÁÅÔ ÄÌÑ ÐÁÎÅÌÉ ÓÔÁÔÕÓÁ ÎÁÓÔÒÏÊËÉ ÐÏ ÕÍÏÌÞÁÎÉÀ, ÉÌÉ ÅÓÌÉ ÏÎÁ ÂÙÌÁ ÓÏÚÄÁÎÁ ×ÁÍÉ, ÔÏ ÕÄÁÌÑÅԠţ.</p>
+
+<p>ðÁÎÅÌØ ÍÏÖÅÔ ÉÍÅÔØ Ä×Á ÔÉÐÁ: windows É root - ÜÔÏ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ÏÎÁ ÍÏÖÅÔ ÂÙÔØ ×ÉÄÎÁ ÄÌÑ ×ÓÅÈ ÏËÏΠÉÌÉ ÔÏÌØËÏ ÄÌÑ ÏÄÎÏÇÏ. 
+Placement - ÜÔÏ ÒÁÓÐÏÌÏÖÅÎÉÅ ÐÁÎÅÌÉ: top - Ó×ÅÒÈÕ, bottom - ÓÎÉÚÕ. 
+Position - ÜÔÏ ÞÉÓÌÏ, ÞÅÍ ÂÏÌØÛÅ ÚÎÁÞÅÎÉÅ ËÏÔÏÒÏÇÏ, ÔÅÍ ÎÉÖÅ ÎÁ ÜËÒÁÎÅ ÒÁÓÐÏÌÁÇÁÅÔÓÑ ÐÁÎÅÌØ. 
+ðÁÒÁÍÅÔÒ Visible ÍÏÖÅÔ ÐÒÉÎÉÍÁÔØ 3 ÚÎÁÞÅÎÉÑ: always, active É inactive. òÅÖÉÍÙ active/inactive ÐÏÌÅÚÎÙ ÔÏÌØËÏ ÄÌÑ ÒÁÚÄÅÌÅÎÎÙÈ ÏËÏÎ. 
+üÔÉ ÎÁÓÔÒÏÊËÉ ÍÏÇÕÔ ÂÙÔØ ÉÚÍÅÎÅÎÙ ÓÌÅÄÕÀÝÉÍÉ ËÏÍÁÎÄÁÍÉ:</p>
+
+<pre>
+/STATUSBAR &lt;ÉÍÑ&gt; TYPE window|root
+/STATUSBAR &lt;ÉÍÑ&gt; PLACEMENT top|bottom
+/STATUSBAR &lt;ÉÍÑ&gt; POSITION &lt;num&gt;
+/STATUSBAR &lt;ÉÍÑ&gt; VISIBLE always|active|inactive
+</pre>
+
+<p>ëÏÇÄÁ ×Ù ÚÁÇÒÕÖÁÅÔÅ ÎÏ×ÙÅ ÓËÒÉÐÔÙ ÄÌÑ ÐÁÎÅÌÅÊ ÓÔÁÔÕÓÁ ×ÁÍ ÓËÏÒÅÅ ×ÓÅÇÏ ÐÒÉÄÅÔÓÑ ×ÙÂÒÁÔØ ÇÄÅ ×Ù ÈÏÔÉÔÅ ÉÈ ÒÁÓÐÏÌÏÖÉÔØ. 
+ëÏÍÐÏÎÅÎÔÙ ÐÁÎÅÌÅÊ ÍÏÇÕÔ ÂÙÔØ ÉÚÍÅÎÅÎÙ ÓÌÅÄÕÀÝÉÍÉ ËÏÍÁÎÄÁÍÉ:</p>
+
+<pre>
+/STATUSBAR &lt;ÉÍÑ&gt; ADD [-before | -after &lt;item&gt;] [-priority #] [-alignment left|right] &lt;ËÏÍÐÏÎÅÎÔÁ(item)&gt;
+/STATUSBAR &lt;ÉÍÑ&gt; REMOVE &lt;ËÏÍÐÏÎÅÎÔÁ(item)&gt;
+</pre>
+
+<p>ïÂÙÞÎÏ ÄÌÑ ÉÍÑ ËÏÍÐÏÎÅÎÔÙ × ÓËÒÉÐÔÅ ÄÌÑ ÐÁÎÅÌÉ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÉÍÅÎÉ ÓËÒÉÐÔÁ. 
+ï ÜÔÏÍ ÄÏÌÖÎÏ ÂÙÔØ ÎÁÐÉÓÁÎÏ × ÄÏËÕÍÅÎÔÁÃÉÉ Ë ÓËÒÉÐÔÕ. ôÁË ÞÔÏ ÞÔÏÂÙ ÄÏÂÁ×ÉÔØ ÓËÒÉÐÔ mail.pl 
+ÐÅÒÅÄ ÓÐÉÓËÏÍ ÁËÔÉ×ÎÙÈ ÏËÏΠ(ÓÍÏÔÒÉÔÅ 
+<code>/STATUSBAR</code>), ××ÅÄÉÔÅ ÜÔÕ ËÏÍÁÎÄÕ: <code>/STATUSBAR window ADD -before
+act mail</code>.</p>
index a5c5d472b82b638d09e169886bd47a04a5edbefc..892ff485d598d26cebf0c0cf2a187467601943f2 100644 (file)
@@ -107,28 +107,30 @@ management (send me a note if you can think of more):</p>
 
 <p>And example how to add servers:</p>
 
-<p>(openprojects network, identify with nickserv and wait for 2 seconds before
+<p>(OFTC network, identify with nickserv and wait for 2 seconds before
 joining channels)</p>
 
 <pre>
-/IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -freenode 2000" freenode
+/NETWORK ADD -autosendcmd "/^msg nickserv ident pass;wait 2000" OFTC
 </pre>
 
-<p>Then add some servers to different networks (ircnet is already set up 
+<p>(NOTE: use /IRCNET with 0.8.9 and older)</p>
+
+<p>Then add some servers to different networks (network 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
+/SERVER ADD -auto -network IRCnet irc.kpnqwest.fi 6667
+/SERVER ADD -network IRCnet irc.funet.fi 6667
+/SERVER ADD -auto -network 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 #irssi IRCnet
 /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
 </pre>
 
@@ -156,9 +158,10 @@ 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>   
+key? ESC key always works as Meta, but there's also easier ways. ALT could
+work as Meta, or if you have Windows keyboard, left Windows key might work
+as Meta. 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
@@ -215,7 +218,7 @@ 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 2: win#3 - IRCnet/#channel1, win#4 - IRCnet/#channel2
 Split window 3: win#5 - efnet/#channel1, win#6 - efnet/#channel2
 </pre>
 
@@ -264,22 +267,22 @@ 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 <code>/IRCNET</code>
-command to see if it's already there. If it isn't, use <code>/IRCNET ADD
-yourircnet</code>. If you want to execute some commands automatically when
+<p>First you need to have your IRC network set, use <code>/NETWORK</code>
+command to see if it's already there. If it isn't, use <code>/NETWORK ADD
+yournetwork</code>. If you want to execute some commands automatically when
 you're connected to some network, use <code>-autosendcmd</code> option.
-Here's some examples:</p>
+(NOTE: use /IRCNET with 0.8.9 and older.) Here's some examples:</p>
 
 <pre>
-/IRCNET ADD -autosendcmd '^msg bot invite' ircnet
-/IRCNET ADD -autosendcmd "/^msg nickserv ident pass;wait -freenode 2000" freenode
+/NETWORK ADD -autosendcmd '^msg bot invite' IRCnet
+/NETWORK ADD -autosendcmd "/^msg nickserv ident pass;wait 2000" OFTC
 </pre>
 
 <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
+/SERVER ADD -auto -network IRCnet irc.kpnqwest.fi 6667
+/SERVER ADD -auto -network worknet irc.mycompany.com 6667 password
 </pre>
 
 <p>The <code>-auto</code> option specifies that this server is
@@ -292,7 +295,7 @@ fails.</p>
 
 <pre>
 /CHANNEL ADD -auto -bots *!*bot@host.org -botcmd "/^msg $0 op pass" #irssi efnet
-/CHANNEL ADD -auto #secret ircnet password
+/CHANNEL ADD -auto #secret IRCnet password
 </pre>
 
 <p><code>-bots</code> and <code>-botcmd</code> should be the only ones
@@ -401,17 +404,17 @@ without any parameters. You should see a list of something like:</p>
 
 <pre>
 -!- IRCNet: irc.song.fi:6667 (IRCNet)
--!- freenode: irc.freenode.net:6667 (freenode)
+-!- OFTC: irc.oftc.net:6667 (OFTC)
 -!- RECON-1: 192.168.0.1:6667 () (02:59 left before reconnecting)
 </pre>
 
-<p>Here you see that we're connected to IRCNet and freenode networks.
+<p>Here you see that we're connected to IRCNet and OFTC networks.
 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
+adds a number after the tag so there could be network, network2, network3
 etc.</p>
 
 <p>Server tags beginning with <code>RECON-</code> mean server
@@ -422,7 +425,7 @@ successful and irssi will try to connect it again in 3 minutes.</p>
 reconnecting, use</p>
 
 <pre>
-/DISCONNECT ircnet   - disconnect server with tag "ircnet"
+/DISCONNECT network   - disconnect server with tag "network"
 /DISCONNECT recon-1  - stop trying to reconnect to RECON-1 server
 /RMRECONNS           - stop all server reconnections
 
@@ -475,12 +478,12 @@ for each server. Here's how to do them (repeat for each server)</p>
 /WINDOW NEW HIDE
 /WINDOW NAME (status)
 /WINDOW LEVEL ALL -MSGS
-/WINDOW SERVER -sticky ircnet
+/WINDOW SERVER -sticky network
 
 /WINDOW NEW HIDE
 /WINDOW NAME (msgs)
 /WINDOW LEVEL MSGS
-/WINDOW SERVER -sticky ircnet
+/WINDOW SERVER -sticky network
 </pre>
 
 <h3><a id="c7">7. /LASTLOG and jumping around in scrollback</a></h3>
@@ -619,8 +622,8 @@ exactly as if you'd want to connect directly to them. Nothing special
 about them:</p>
 
 <pre>
-/SERVER ADD -auto -ircnet dalnet irc.dal.net
-/SERVER ADD -auto -ircnet efnet irc.efnet.org
+/SERVER ADD -auto -network dalnet irc.dal.net
+/SERVER ADD -auto -network efnet irc.efnet.org
 </pre>
 
 <p>With the proxy <code>/SET</code>s however, irssi now connects to those
@@ -647,7 +650,7 @@ you can just forget that your bouncer even exists.</p>
 
 <pre>
 /SET -clear proxy_password
-/EVAL SET proxy_string CONNECT %s:%d\n\n
+/EVAL SET proxy_string CONNECT %s:%d HTTP/1.0\n\n
 </pre>
 
 <p><strong>BNC</strong></p>
@@ -661,16 +664,16 @@ you can just forget that your bouncer even exists.</p>
 <p><strong>dircproxy</strong></p>
 
 <p>dircproxy separates the server connections by passwords. So, if you
-for example have ircnet connection with password ircpass and
-openprojects connection with freenodepass, you would do something like
+for example have network connection with password ircpass and
+OFTC connection with oftcpass, you would do something like
 this:</p>
 
 <pre>
 /SET -clear proxy_password
 /SET -clear proxy_string
 
-/SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
-/SERVER ADD -auto -ircnet freenode fake.freenode 6667 freenodepass
+/SERVER ADD -auto -network IRCnet fake.network 6667 ircpass
+/SERVER ADD -auto -network OFTC fake.oftc 6667 oftcpass
 </pre>
 
 <p>The server name and port you give isn't used anywhere, so you can
@@ -687,15 +690,17 @@ as with dircproxy, by creating fake connections:</p>
 /SET -clear proxy_password
 /SET -clear proxy_string
 
-/IRCNET ADD -user ircnetuser ircnet
-/SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
-/IRCNET ADD -user freenodeuser freenode
-/SERVER ADD -auto -ircnet freenode fake.freenode 6667 freenodepass
+/NETWORK ADD -user networkuser IRCnet
+/SERVER ADD -auto -network IRCnet fake.network 6667 ircpass
+/NETWORK ADD -user oftcuser OFTC
+/SERVER ADD -auto -network OFTC fake.oftc 6667 oftcpass
 </pre>
 
-<p>So, you'll specify the usernames with <code>/IRCNET ADD</code> command,
+<p>So, you'll specify the usernames with <code>/NETWORK ADD</code> command,
 and the user's password with <code>/SERVER ADD</code>.</p>
 
+<p>(NOTE: use /IRCNET with 0.8.9 and older.)</p>
+
 <p><strong>Irssi proxy</strong></p>
 
 <p>Irssi contains it's own proxy which you can build giving
@@ -711,19 +716,19 @@ anyone figure out even more easier ways to say this, so I wouldn't need to
 try to explain this thing for minutes every time? :)</p>
 
 <p>Irssi proxy supports sharing multiple server connections in different
-ports, like you can share ircnet in port 2777 and efnet in port 2778.</p>
+ports, like you can share network in port 2777 and efnet in port 2778.</p>
 
 <p>Usage in proxy side:</p>
 
 <pre>
 /LOAD proxy
 /SET irssiproxy_password &lt;password&gt;
-/SET irssiproxy_ports &lt;ircnet&gt;=&lt;port&gt; ... (eg. ircnet=2777 efnet=2778)
+/SET irssiproxy_ports &lt;network&gt;=&lt;port&gt; ... (eg. IRCnet=2777 efnet=2778)
 </pre>
 
 <p><strong>NOTE</strong>: you <strong>MUST</strong> add all the servers you
-are using to server and ircnet lists with <code>/SERVER ADD</code> and 
-<code>/IRCNET ADD</code>. ..Except if you really don't want to for some
+are using to server and network lists with <code>/SERVER ADD</code> and 
+<code>/NETWORK ADD</code>. ..Except if you really don't want to for some
 reason, and you only use one server connection, you may simply set:</p>
 
 <pre>
@@ -736,8 +741,8 @@ reason, and you only use one server connection, you may simply set:</p>
 specified in <code>/SET irssiproxy_password</code>. For example:</p>
 
 <pre>
-/SERVER ADD -ircnet ircnet my.irssi-proxy.org 2777 secret
-/SERVER ADD -ircnet efnet my.irssi-proxy.org 2778 secret
+/SERVER ADD -network IRCnet my.irssi-proxy.org 2777 secret
+/SERVER ADD -network efnet my.irssi-proxy.org 2778 secret
 </pre>
 
 <p>Irssi proxy works fine with other IRC clients as well.</p>
@@ -784,7 +789,7 @@ of them you might want to change (the default value is shown):</p>
 <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 <code>/SET -clear autocrate_query_level</code>.</dd>
+  disable this with <code>/SET -clear autocreate_query_level</code>.</dd>
 
 <dt>/SET autoclose_query 0</dt>
   <dd>Query windows can be automatically closed after certain time of
index 0c1ae85609eb9d129ef1cc5ae0247d34f29d6648..d6413ff76cb0940d6e90c1ab71ff752e88f1c117 100644 (file)
@@ -5,7 +5,7 @@ dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [
 dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject or 
 dnl gthread is specified in MODULES, pass to pkg-config
 dnl
-AC_DEFUN(AM_PATH_GLIB_2_0,
+AC_DEFUN([AM_PATH_GLIB_2_0],
 [dnl 
 dnl Get the cflags and libraries from pkg-config
 dnl
@@ -19,6 +19,9 @@ AC_ARG_ENABLE(glibtest, [  --disable-glibtest      do not try to compile and run
          gmodule) 
              pkg_config_args="$pkg_config_args gmodule-2.0"
          ;;
+         gmodule-no-export) 
+             pkg_config_args="$pkg_config_args gmodule-no-export-2.0"
+         ;;
          gobject) 
              pkg_config_args="$pkg_config_args gobject-2.0"
          ;;
@@ -173,6 +176,8 @@ main ()
         :
        else
           echo "*** Could not run GLIB test program, checking why..."
+          ac_save_CFLAGS="$CFLAGS"
+          ac_save_LIBS="$LIBS"
           CFLAGS="$CFLAGS $GLIB_CFLAGS"
           LIBS="$LIBS $GLIB_LIBS"
           AC_TRY_LINK([
@@ -189,9 +194,7 @@ main ()
           echo "*** If you have an old version installed, it is best to remove it, although"
           echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ],
         [ echo "*** The test program failed to compile or link. See the file config.log for the"
-          echo "*** exact error that occured. This usually means GLIB was incorrectly installed"
-          echo "*** or that you have moved GLIB since it was installed. In the latter case, you"
-          echo "*** may want to edit the pkg-config script: $PKG_CONFIG" ])
+          echo "*** exact error that occured. This usually means GLIB is incorrectly installed."])
           CFLAGS="$ac_save_CFLAGS"
           LIBS="$ac_save_LIBS"
        fi
index 38cc48267c9ffbbdee3d771a7bf27c43f55980d6..5e569253ac8b3cd0df9c4519f74cb3ecd141f5c2 100644 (file)
@@ -1,13 +1,18 @@
-scriptdir = $(datadir)/silc/scripts
+SUBDIRS = examples
+
+scriptdir = $(datadir)/irssi/scripts
 
 script_DATA = \
        autoop.pl \
-       clones.pl \
-       hello.pl \
-       mail.pl \
-       beep.pl \
-       dns.pl  \
-       mail-maildir.pl \
-       silc-mime.pl
+       autorejoin.pl \
+       buf.pl \
+       dns.pl \
+       kills.pl \
+       mail.pl \
+       mlock.pl \
+       quitmsg.pl \
+       scriptassist.pl \
+       splitlong.pl \
+       usercount.pl
 
 EXTRA_DIST = $(script_DATA)
index 413f5e171da0adca4742d97bd6323bc0799e7cea..f718299911e1f11d850f43e98aefc690d7a39899 100644 (file)
@@ -1,7 +1,18 @@
 # /AUTOOP <*|#channel> [<nickmasks>]
+# use friends.pl if you need more features
 
 use Irssi;
 use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'autoop',
+    description => 'Simple auto-op script',
+    license     => 'Public Domain',
+    changed    => 'Sun Mar 10 23:18 EET 2002'
+);
 
 my (%opnicks, %temp_opped);
 
@@ -53,7 +64,7 @@ sub autoop {
 
                 if (!$temp_opped{$nick} &&
                    $server->masks_match($masks, $nick, $host)) {
-                       $channel->command("/op $nick");
+                       $channel->command("op $nick");
                        $temp_opped{$nick} = 1;
                }
        }
diff --git a/apps/irssi/scripts/autorejoin.pl b/apps/irssi/scripts/autorejoin.pl
new file mode 100644 (file)
index 0000000..2d23449
--- /dev/null
@@ -0,0 +1,52 @@
+# automatically rejoin to channel after kicked
+
+# /SET autorejoin_channels #channel1 #channel2 ...
+
+# NOTE: I personally don't like this feature, in most channels I'm in it
+# will just result as ban. You've probably misunderstood the idea of /KICK
+# if you kick/get kicked all the time "just for fun" ...
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'autorejoin',
+    description => 'Automatically rejoin to channel after kicked',
+    license     => 'Public Domain',
+    changed    => 'Sun Mar 10 23:18 EET 2002'
+);
+
+sub channel_rejoin {
+  my ($server, $channel) = @_;
+
+  # check if channel has password
+  my $chanrec = $server->channel_find($channel);
+  my $password = $chanrec->{key} if ($chanrec);
+
+  # We have to use send_raw() because the channel record still
+  # exists and irssi won't even try to join to it with command()
+  $server->send_raw("JOIN $channel $password");
+}
+
+sub event_rejoin_kick {
+  my ($server, $data) = @_;
+  my ($channel, $nick) = split(/ +/, $data);
+
+  return if ($server->{nick} ne $nick);
+
+  # check if we want to autorejoin this channel
+  my @chans = split(/[ ,]+/, Irssi::settings_get_str('autorejoin_channels'));
+  foreach my $chan (@chans) {
+    if (lc($chan) eq lc($channel)) {
+      channel_rejoin($server, $channel);
+      last;
+    }
+  }
+}
+
+Irssi::settings_add_str('misc', 'autorejoin_channels', '');
+Irssi::signal_add('event kick', 'event_rejoin_kick');
diff --git a/apps/irssi/scripts/buf.pl b/apps/irssi/scripts/buf.pl
new file mode 100644 (file)
index 0000000..43b4b3d
--- /dev/null
@@ -0,0 +1,122 @@
+use strict;
+use vars qw($VERSION %IRSSI);
+
+use Irssi qw(command signal_add signal_add_first active_win
+             settings_get_str settings_get_bool channels windows
+            settings_add_str settings_add_bool get_irssi_dir
+            window_find_refnum signal_stop);
+$VERSION = '2.13';
+%IRSSI = (
+    authors    => 'Juerd',
+    contact    => 'juerd@juerd.nl',
+    name       => 'Scroll buffer restorer',
+    description        => 'Saves the buffer for /upgrade, so that no information is lost',
+    license    => 'Public Domain',
+    url                => 'http://juerd.nl/irssi/',
+    changed    => 'Mon May 13 19:41 CET 2002',
+    changes    => 'Severe formatting bug removed * oops, I ' .
+                   'exposed Irssi to ircII foolishness * sorry ' .
+                  '** removed logging stuff (this is a fix)',
+    note1      => 'This script HAS TO BE in your scripts/autorun!',
+    note2      => 'Perl support must be static or in startup',
+);
+
+# Q: How can I get a very smooth and clean upgrade?
+#
+# A: /set -clear upgrade_separator
+#    /set upgrade_suppress_join ON (default)
+#    /set channel_sync OFF
+
+# Q: Can I use color in the upgrade_separator?
+# Q: Is it possible to save my command history?
+# Q: Can I prevent the screen from blinking?
+# Q: Can you make it faster?
+#
+# A: Probably not, but if you can do it, tell me how.
+
+use Irssi::TextUI;
+use Data::Dumper;
+
+my %suppress;
+
+sub upgrade {
+    open BUF, sprintf('>%s/scrollbuffer', get_irssi_dir) or die $!;
+    print BUF join("\0", map $_->{server}->{address} . $_->{name}, channels), "\n";
+    for my $window (windows) {
+       next unless defined $window;
+       next if $window->{name} eq 'status';
+       my $view = $window->view;
+       my $line = $view->get_lines;
+       my $lines  = 0;
+       my $buf = '';
+       if (defined $line){
+           {
+               $buf .= $line->get_text(1) . "\n";
+               $line = $line->next;
+               $lines++;
+               redo if defined $line;
+           }
+       }
+       printf BUF "%s:%s\n%s", $window->{refnum}, $lines, $buf;
+    }
+    close BUF;
+    unlink sprintf("%s/sessionconfig", get_irssi_dir);
+    command 'layout save';
+    command 'save';
+}
+
+sub restore {
+    open BUF, sprintf('<%s/scrollbuffer', get_irssi_dir) or die $!;
+    my @suppress = split /\0/, <BUF>;
+    if (settings_get_bool 'upgrade_suppress_join') {
+       chomp $suppress[-1];
+       @suppress{@suppress} = (2) x @suppress;
+    }
+    active_win->command('^window scroll off');
+    while (my $bla = <BUF>){
+       chomp $bla;
+       my ($refnum, $lines) = split /:/, $bla;
+       next unless $lines;
+       my $window = window_find_refnum $refnum;
+       unless (defined $window){
+           <BUF> for 1..$lines;
+           next;
+       }
+       my $view = $window->view;
+       $view->remove_all_lines();
+       $view->redraw();
+       my $buf = '';
+       $buf .= <BUF> for 1..$lines;
+       my $sep = settings_get_str 'upgrade_separator';
+       $sep .= "\n" if $sep ne '';
+       $window->gui_printtext_after(undef, MSGLEVEL_CLIENTNOTICE, "$buf\cO$sep");
+       $view->redraw();
+    }
+    active_win->command('^window scroll on');
+    active_win->command('^scrollback end');
+}
+
+sub suppress {
+    my ($first, $second) = @_;
+    return
+       unless scalar keys %suppress
+       and settings_get_bool 'upgrade_suppress_join';
+    my $key = $first->{address} . 
+       (grep { (s/^://, /^[#!+&]/) } split ' ', $second)[0];
+    if (exists $suppress{$key} and $suppress{$key}--) {
+       signal_stop();
+       delete $suppress{$key} unless $suppress{$key};
+    }
+}
+
+settings_add_str  'buffer', 'upgrade_separator'     => '=Upgrade=';
+settings_add_bool 'buffer', 'upgrade_suppress_join' => 1;
+
+signal_add_first 'session save'    => 'upgrade';
+signal_add_first 'session restore' => 'restore';
+signal_add       'event 366'       => 'suppress';
+signal_add       'event join'      => 'suppress';
+
+unless (-f sprintf('%s/scripts/autorun/buf.pl', get_irssi_dir)) {
+    Irssi::print('PUT THIS SCRIPT IN ~/.irssi/scripts/autorun/ BEFORE /UPGRADING!!');
+}
index 01a12f24a65012765afc61e6287fd499e625875a..612fab0efd9ca053e67e82758a9378ab7f12062f 100644 (file)
@@ -1,11 +1,20 @@
 # /DNS <nick>|<host>|<ip> ...
-# for irssi 0.7.99 by Timo Sirainen
-# version 2.0
 
+use Irssi;
 use strict;
 use Socket;
 use POSIX;
 
+use vars qw($VERSION %IRSSI); 
+$VERSION = "2.1";
+%IRSSI = (
+    authors    => 'Timo Sirainen',
+    name       => 'dns',
+    description        => '/DNS <nick>|<host>|<ip> ...',
+    license    => 'Public Domain',
+    changed    => 'Sun Mar 10 23:23 EET 2002'
+);
+
 my (%resolve_hosts, %resolve_nicks, %resolve_print); # resolve queues
 my $userhosts; # number of USERHOSTs currently waiting for reply
 my $lookup_waiting; # 1 if we're waiting a reply for host lookup
@@ -25,6 +34,7 @@ sub cmd_dns {
   my $ask_nicks = "";
   my $print_error = 0;
   foreach my $nick (split(" ", $nicks)) {
+    $nick = lc($nick);
     if ($nick =~ /[\.:]/) {
       # it's an IP or hostname
       $resolve_hosts{$nick} = $tag;
@@ -66,9 +76,9 @@ sub sig_userhost {
   # move resolve_nicks -> resolve_hosts
   foreach my $host (@hosts) {
     if ($host =~ /^([^=\*]*)\*?=.(.*)@(.*)/) {
-      my $nick = $1;
+      my $nick = lc($1);
       my $user = $2;
-      $host = $3;
+      $host = lc($3);
 
       $resolve_hosts{$host} = $resolve_nicks{$nick};
       delete $resolve_nicks{$nick};
diff --git a/apps/irssi/scripts/examples/Makefile.am b/apps/irssi/scripts/examples/Makefile.am
new file mode 100644 (file)
index 0000000..c8d8c8e
--- /dev/null
@@ -0,0 +1,8 @@
+scriptdir = $(datadir)/irssi/scripts
+
+script_DATA = \
+       command.pl \
+       msg-event.pl \
+       redirect.pl
+
+EXTRA_DIST = $(script_DATA)
diff --git a/apps/irssi/scripts/examples/command.pl b/apps/irssi/scripts/examples/command.pl
new file mode 100644 (file)
index 0000000..77a9d7f
--- /dev/null
@@ -0,0 +1,23 @@
+# Example how to create your own /commands:
+
+# /HELLO <nick> - sends a "Hello, world!" to given nick.
+
+use Irssi;
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'command',
+    description => 'Command example',
+    license     => 'Public Domain'
+);
+
+sub cmd_hello {
+       my ($data, $server, $channel) = @_;
+
+       $server->command("/msg $data Hello, world!");
+}
+
+Irssi::command_bind('hello', 'cmd_hello');
diff --git a/apps/irssi/scripts/examples/msg-event.pl b/apps/irssi/scripts/examples/msg-event.pl
new file mode 100644 (file)
index 0000000..53174e2
--- /dev/null
@@ -0,0 +1,41 @@
+# Example how to react on specific messages:
+
+# !reverse <text> sends back the text reversed.
+
+use Irssi;
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'msg-event',
+    description => 'Event example',
+    license     => 'Public Domain'
+);
+
+sub event_privmsg {
+       # $server = server record where the message came
+       # $data = the raw data received from server, with PRIVMSGs it is:
+       #         "target :text" where target is either your nick or #channel
+       # $nick = the nick who sent the message
+       # $host = host of the nick who sent the message
+       my ($server, $data, $nick, $host) = @_;
+
+       # split data to target/text
+       my ($target, $text) = $data =~ /^(\S*)\s:(.*)/;
+
+       # skip lines not beginning with !reverse
+       return if ($text !~ /!reverse (.*)/);
+       $text = $1;
+
+       if (!$server->ischannel($target)) {
+               # private message, $target contains our nick, so we'll need
+               # to change it to $nick
+               $target = $nick;
+       }
+
+       $server->command("notice $target reversed $text = ".reverse($text));
+}
+
+Irssi::signal_add('event privmsg', 'event_privmsg');
diff --git a/apps/irssi/scripts/examples/redirect.pl b/apps/irssi/scripts/examples/redirect.pl
new file mode 100644 (file)
index 0000000..6a7e94c
--- /dev/null
@@ -0,0 +1,40 @@
+# Example how to do redirections, we'll grab the output of /WHOIS:
+
+# /RN - display real name of nick
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'redirect',
+    description => 'Redirection example',
+    license     => 'Public Domain'
+);
+
+sub cmd_realname {
+       my ($data, $server, $channel) = @_;
+
+       # ignore all whois replies except "No such nick" or the 
+       # first line of the WHOIS reply
+       $server->redirect_event('whois', 1, $data, -1, '', {
+                         'event 402' => 'event 402',
+                         'event 401' => 'event 401',
+                         'event 311' => 'redir whois',
+                         '' => 'event empty' });
+
+       $server->send_raw("WHOIS :$data");
+}
+
+sub event_rn_whois {
+       my ($num, $nick, $user, $host, $empty, $realname) = split(/ +/, $_[1], 6);
+       $realname =~ s/^://;
+
+       Irssi::print("%_$nick%_ is $realname");
+}
+
+Irssi::command_bind('rn', 'cmd_realname');
+Irssi::signal_add('redir whois', 'event_rn_whois');
diff --git a/apps/irssi/scripts/kills.pl b/apps/irssi/scripts/kills.pl
new file mode 100644 (file)
index 0000000..7ed2d53
--- /dev/null
@@ -0,0 +1,102 @@
+# Display kills with more understandable messages.
+# for irssi 0.7.98 by Timo Sirainen
+
+# There's one kind of nick collision this script doesn't handle - if the
+# collision is detected by the server you're connected to, it won't use
+# kill as quit reason, but "Nick collision(new)" or "..(old)". This is pretty
+# easy to understand already, happens hardly ever(?) and it can be faked
+# so I thought better not change it to kill message.
+
+# There's a pretty good explanation of (ircnet) ircd's server kills in
+# http://www.irc.org/tech_docs/ircnet/kills.html
+
+use Irssi;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors    => 'Timo Sirainen',
+    name       => 'kills',
+    description        => 'Displays kills with more understandable messages',
+    license    => 'Public Domain',
+    changed    => 'Sun Mar 10 23:18 EET 2002'
+);
+
+Irssi::theme_register([
+  'kill_public', '{channick $0} {chanhost $1} killed by {nick $2}$3 {reason $4}'
+]);
+
+sub msg_quit {
+  my ($server, $nick, $addr, $data) = @_;
+
+  my $localkill;
+  if ($data =~ /^Killed \(([^ ]*) \((.*)\)\)$/) {
+    # remote kill
+    $localkill = 0;
+  } elsif ($data =~ /^Local Kill by ([^ ]*) \((.*)\)/) {
+    # local kill
+    $localkill = 1;
+  } else {
+    return;
+  }
+
+  my $killer = $1;
+  my $killmsg = $2;
+  my $msg = "\002Nick collision\002: ";
+
+  my @printargs = ();
+  if ($killmsg =~ /([^ ]*) != (.*)/) {
+    # 1 != 2
+    my $server1 = $1, $server2 = $2;
+
+    $server1 =~ s/([^\[]*)\[([^\]]*)\]/\1/;
+    $msg .= "$2 != $server2";
+  } elsif ($killmsg =~ /([^ ]*) <- (.*)/) {
+    # 1 <- 2
+    my $server1 = $1, $server2 = $2;
+
+    if ($server1 =~ /^\(/) {
+      # (addr1)server1 <- (add2)server2
+      $server1 =~ s/^\(([^\)]*)\)//;
+      my $nick1 = $1;
+      $server2 =~ s/^\(([^\)]*)\)//;
+      my $nick2 = $1;
+
+      $msg .= "server $server1";
+      $msg .= " (nick from $nick1)" if $nick1;
+      $msg .= " <- ";
+      $msg .= "\002$server2\002";
+      $msg .= " (nick from \002$nick2\002)" if $nick2;
+    } elsif ($server1 =~ /\)$/ || $server2 =~ /\)$/) {
+      # server1(nick) <- server2
+      # server1 <- server2(nick)
+      $server1 =~ s/\(([^\)]*)\)$//;
+      my $oldnick = $1;
+      $server2 =~ s/\(([^\)]*)\)$//;
+      $oldnick = $1 if $1;
+      $msg = "\002Nick change collision\002: $server1 <- \002$server2\002 (old nick \002$oldnick\002)";
+    } else {
+      # server1 <- server2
+      $msg = "\002Nick/server collision\002: $server1 <- \002$server2\002";
+    }
+  } else {
+    # something else, just show it as-is
+    $msg = $killmsg;
+  }
+
+  @list = $server->nicks_get_same($nick);
+  while (@list) {
+    $channel = $list[0];
+    shift @list;
+    # skip nick record
+    shift @list;
+
+    $channel->printformat(MSGLEVEL_QUITS, 'kill_public',
+                          $nick, $addr, $killer,
+                         $localkill ? " (local)" : "", $msg);
+  }
+
+  Irssi::signal_stop();
+}
+
+Irssi::signal_add('message quit', 'msg_quit');
index 2d11a46f8eb2c4384fc129419ff05127fa60d936..33b3c22e591522f5d80edca83c1074b26a9d7819 100644 (file)
+$VERSION = "2.92";
+%IRSSI = (
+    authors     => "Timo Sirainen, Matti Hiljanen, Joost Vunderink, Bart Matthaei",
+    contact     => "tss\@iki.fi, matti\@hiljanen.com, joost\@carnique.nl, bart\@dreamflow.nl",
+    name        => "mail",
+    description => "Fully customizable mail counter statusbar item with multiple mailbox and multiple Maildir support",
+    license     => "Public Domain",
+    url         => "http://irssi.org, http://scripts.irssi.de",
+);
+
 # Mail counter statusbar item
-# for irssi 0.7.99 by Timo Sirainen
-#  /SET mail_ext_program - specify external mail checker program
-#  /SET mail_file - specifies mbox file location
-#  /SET mail_refresh_time - in seconds, how often to check for new mail
+# for irssi 0.8.1 by Timo Sirainen
+#
+# Maildir support added by Matti Hiljanen
+# Multiple Maildir/mbox and customization support added by Joost Vunderink
+# OLD mailtreatment switch added by Bart Matthaei.
+# Improved some regexps in maildirmode by Bart Matthaei.
+# Maildirmode regexps (hopefully) fixed for good by Matti Hiljanen. 
+#
+# You can add any number of mailboxes or Maildirs to watch for new mail in.
+# Give them any name and <name>:<count> will appear in your mail
+# statusbar item, where <count> is the number of unread messages.
+# If only 1 mailbox/Maildir is defined, the statusbar item will have the 
+# familiar form [Mail: <count>].
+# If you set mail_show_message to ON, irssi will print a message in the
+# active window whenever new mail arrives.
+#
+# Check /mailbox help for help.
 
-use strict;
 use Irssi::TextUI;
 
+my $maildirmode = 0; # maildir=1, file(spools)=0
+my $old_is_not_new = 0; 
 my $extprog;
 my ($last_refresh_time, $refresh_tag);
 
 # for mbox caching
-my ($last_size, $last_mtime, $last_mailcount);
+my $last_size, $last_mtime, $last_mailcount, $last_mode;
 
-sub mbox_count {
-  my $mailfile = shift;
+# list of mailboxes
+my %mailboxes = (); 
+my %new_mails_in_box = ();
+my $nummailboxes = 0; 
 
-  my @stat = stat($mailfile);
-  my $size = $stat[7];
-  my $mtime = $stat[9];
+# the string to be stored in Irssi's mail_mailboxes setting
+my $mailboxsetting = "";
 
-  # if the file hasn't changed, get the count from cache
-  return $last_mailcount if ($last_size == $size && $last_mtime == $mtime);
-  $last_size = $size;
-  $last_mtime = $mtime;
+sub cmd_print_help {
+  Irssi::print(
+  "MAILBOX ADD <num> <file|dir>\n".
+  "MAILBOX DEL <num>\n".
+  "MAILBOX SHOW\n\n".
+  "Statusbar item to keep track of how many (new) emails there are in ".
+  "each of your mailboxes/Maildirs.\n\n".
+  "/MAILBOX ADD <name> <file|dir>\n".
+  "    - Adds a mailbox or a Maildir to the list.\n".
+  "/MAILBOX DEL <name>\n".
+  "    - Removes mailbox or Maildir named <name> from the list.\n".
+  "/MAILBOX SHOW\n".
+  "    - Shows a list of the defined mailboxes.\n\n".
+  "Use the following commands to change the behaviour:\n\n".
+  "/SET MAILDIRMODE on|off\n".
+  "    - If maildirmode is on, the mailboxes in the list are assumed to be ".
+  "directories. Otherwise they are assumed to be spool files.\n".
+  "      Default: off.\n".
+  "/SET MAIL_OLDNOTNEW on|off\n".
+  "    - If switched on, mail marked als \"OLD\" will not be treated as new.\n".
+  "      Default: off.\n".
+  "/SET MAIL_EXT_PROGRAM <prog>\n".
+  "    - <prog> will be used to check for mail.\n".
+  "/SET MAIL_REFRESH_TIME <num>\n".
+  "    - Sets the time between checks to <num> seconds.\n      Default: 60.\n".
+  "/SET MAIL_SHOW_MESSAGE on|off\n".
+  "    - If this is on, a message will be printed in the active window ".
+  "whenever new email is received.\n      Default: off.\n".
+  "/SET MAIL_SHOW_ONLY_UNREAD on|off\n".
+  "    - If you don't want to see a mailbox if it does not contain any new ".
+  "mail, set this to on.\n      Default: on.\n" .
+  "/SET MAIL_SEPARATOR <char>\n".
+  "    - Sets the character to be printed between each mailbox.\n".
+  "      The default is a comma.\n".
+  "/SET MAIL_FORMAT <format>\n".
+  "    - Sets the format of each mailbox.\n".
+  "      Allowed variables:\n".
+  "      %%n = mailbox name\n".
+  "      %%u = number of unread mail\n".
+  "      %%r = number of read mail\n".
+  "      %%t = total amount of mail\n".
+  "      The default format is %%n:%%u/%%t.\n".
+  "\nSee also: STATUSBAR"
+  ,MSGLEVEL_CRAP);
+}
+
+sub mbox_count {
+  my $mailfile = shift;
+  my $unread = 0;
+  my $read = 0;
+  my $maildirmode=Irssi::settings_get_bool('maildir_mode');
+  my $old_is_not_new=Irssi::settings_get_bool('mail_oldnotnew');
 
-  my $count;
   if ($extprog ne "") {
-    $count = `$extprog`;
-    chomp $count;
+     $total = `$extprog`;
+     chomp $unread;
   } else {
-    return 0 if (!open(F, $mailfile));
+    if (!$maildirmode) {
+      if (-f $mailfile) {
+        my @stat = stat($mailfile);
+       my $size = $stat[7];
+       my $mtime = $stat[9];
+
+       # if the file hasn't changed, get the count from cache
+       return $last_mailcount if ($last_size == $size && $last_mtime == $mtime);
+       $last_size = $size;
+       $last_mtime = $mtime;
+
+       my $f = gensym;
+       return 0 if (!open($f, $mailfile));
 
-    $count = 0;
-    while (<F>) {
-      $count++ if (/^From /);
-      $count-- if (/^Subject: .*FOLDER INTERNAL DATA/);
+       # count new mails only
+       my $internal_removed = 0;
+       while (<$f>) {
+         $unread++ if (/^From /);
+
+         if(!$old_is_not_new) {
+               $unread-- if (/^Status: R/);
+         } else {
+               $unread-- if (/^Status: [OR]/);
+         }
+
+         $read++ if (/^From /);
+
+         # Remove folder internal data, but only once
+         if (/^Subject: .*FOLDER INTERNAL DATA/) {
+           if ($internal_removed == 0) {
+             $internal_removed = 1;
+             $read--;
+             $unread--;
+           }
+         }
+       }
+       close($f);
+      }
+    } else {
+      opendir(DIR, "$mailfile/cur") or return 0;
+      while (defined(my $file = readdir(DIR))) {
+        next if $file =~ /^(.|..)$/;
+        # Maildir flags: http://cr.yp.to/proto/maildir.html
+        # My old regexps were useless if the MUA added any 
+        # non-default flags -qvr
+        # 
+        # deleted mail
+        next if $file =~ /\:.*?T.*?$/;
+           if($old_is_not_new) {
+           # when mail gets moved from new to cur it's name _always_
+           # changes from uniq to uniq:info, even when it's still not
+           # read. I assume "old mail" means mail which hasn't been read
+           # yet but it has been "acknowledged" by the user. (it's been
+           # moved to cur) -qvr
+           if ($file =~ /\:.*?$/) {
+              $read++;
+                 next;
+           }
+        } else {
+           if ($file =~ /\:.*?S.*?$/) {
+              $read++;
+                 next;
+           }
+        }
+        $unread++;
+      }
+      closedir(DIR);
+
+      opendir(DIR, "$mailfile/new") or return 0;
+      while (defined(my $file = readdir(DIR))) {
+        next if $file =~ /^(.|..)$/;
+        $unread++;
+      }
+      closedir(DIR);
     }
-    close(F);
   }
 
-  $last_mailcount = $count;
-  return $count;
+  if ($unread eq "" || $unread < 0) {
+    $unread = 0;
+  }
+  if ($read eq "" || $read < 0) {
+    $read = 0;
+  }
+
+  $last_mailcount = $unread;
+
+  return ($unread, $read);
 }
 
+# Checks for mail and sets the statusbar item to the right string.
+# Also shows a message in the active window if that setting is set.
 sub mail {
   my ($item, $get_size_only) = @_;
 
-  my $count = mbox_count(Irssi::settings_get_str('mail_file'));
-  if ($count == 0) {
+  my $result;
+  my $format = Irssi::settings_get_str('mail_format');
+  my $unread = 0;
+  my $read = 0;
+  my $total = 0;
+
+  # check all mailboxes for new email
+  foreach $name (keys(%mailboxes)) {
+    my $box = $mailboxes{$name};
+    # replace "~/" at the beginning by the user's home dir
+    $box =~ s/^~\//$ENV{'HOME'}\//;
+
+    ($unread, $read) = mbox_count($box);
+    $unread = "0" if ($unread eq "");
+    $read = "0" if ($read eq "");
+    $total = $unread + $read;
+    $total = "0" if ($total eq "");
+
+    next if (Irssi::settings_get_bool('mail_show_only_unread') && $unread == 0);
+
+    if ($total eq "") { $total = 0; }
+    if (length($result) > 0) {
+      $result .= Irssi::settings_get_str('mail_separator');
+    }
+    my $string = $format;
+    $string =~ s/%n/$name/;
+    $string =~ s/%u/$unread/;
+    $string =~ s/%r/$read/;
+    $string =~ s/%t/$total/;
+    $result .= $string;
+    
+    # Show -!- You have <num> new messages in <name>.
+    # Show this only if there are any new, unread messages.
+    if (Irssi::settings_get_bool('mail_show_message') &&
+        $unread > $new_mails_in_box{$name}) {
+      $new_mails = $unread - $new_mails_in_box{$name};
+      if ($nummailboxes == 1) {
+        Irssi::print("You have $new_mails new message" . ($new_mails != 1 ? "s." : "."), MSGLEVEL_CRAP);
+      } else {
+        Irssi::print("You have $new_mails new message" . ($new_mails != 1 ? "s " : " ") . "in $name.", MSGLEVEL_CRAP);
+      }
+    }
+
+    $new_mails_in_box{$name} = $unread;
+  }
+  
+  if (length($result) == 0) {
     # no mail - don't print the [Mail: ] at all
     if ($get_size_only) {
       $item->{min_size} = $item->{max_size} = 0;
     }
   } else {
-    $item->default_handler($get_size_only, undef, $count, 1);
+    $item->default_handler($get_size_only, undef, $result, 1);
   }
 }
 
@@ -62,22 +256,159 @@ sub refresh_mail {
   Irssi::statusbar_items_redraw('mail');
 }
 
+# Adds the mailboxes from a string. Only to be used during startup.
+sub add_mailboxes {
+  my $boxstring = $_[0];
+  my @boxes = split(/,/, $boxstring);
+
+  foreach $dbox(@boxes) {
+    my $name = $dbox;
+    $name = substr($dbox, 0, index($dbox, '='));
+    my $box = $dbox;
+    $box = substr($dbox, index($dbox, '=') + 1, length($dbox));
+    addmailbox($name, $box);
+  }
+}
+
+sub addmailbox {
+  my ($name, $box) = @_;
+
+  if (exists($mailboxes{$name})) {
+    if ($box eq $mailboxes{$name}) {
+      Irssi::print("Mailbox $name already set to $box", MSGLEVEL_CRAP);
+    } else {
+      Irssi::print("Mailbox $name changed to $box", MSGLEVEL_CRAP);
+      $new_mails_in_box{$name} = 0;
+    }
+  } else {
+    Irssi::print("Mailbox $name added: " . $box, MSGLEVEL_CRAP);
+    $new_mails_in_box{$name} = 0;
+    $nummailboxes++;
+  }
+  $mailboxes{$name} = $box;
+}
+
+sub delmailbox {
+  my $name = $_[0];
+
+  if (exists($mailboxes{$name})) {
+    Irssi::print("Mailbox $name removed", MSGLEVEL_CRAP);
+    delete($mailboxes{$name});
+    delete($new_mails_in_box{$name});
+    $nummailboxes--;
+  } else {
+    Irssi::print("No such mailbox $name. Use /mailbox show to see a list.", MSGLEVEL_CRAP);
+  }
+}
+
+sub update_settings_string {
+  my $setting;
+
+  foreach $name (keys(%mailboxes)) {
+    $setting .= $name . "=" . $mailboxes{$name} . ",";
+  }
+
+  Irssi::settings_set_str("mail_mailboxes", $setting);
+}
+
+sub cmd_addmailbox {
+  my ($name, $box) = split(/ +/, $_[0]);
+
+  if ($name eq "" || $box eq "") {
+    Irssi::print("Use /mailbox add <name> <mailbox> to add a mailbox.", MSGLEVEL_CRAP);
+    return;
+  }
+
+  addmailbox($name, $box);
+  update_settings_string();
+  refresh_mail();
+}
+
+sub cmd_delmailbox {
+  my $name = $_[0];
+
+  if ($name eq "") {
+    Irssi::print("Use /mailbox del <name> to delete a mailbox.", MSGLEVEL_CRAP);
+    return;
+  }
+
+  delmailbox($name);
+  update_settings_string();
+  refresh_mail();
+}
+
+sub cmd_showmailboxes {
+  if ($nummailboxes == 0) {
+    Irssi::print("No mailboxes defined.", MSGLEVEL_CRAP);
+    return;
+  }
+  Irssi::print("Mailboxes:", MSGLEVEL_CRAP);
+  foreach $box (keys(%mailboxes)) {
+    Irssi::print("$box: " . $mailboxes{$box}, MSGLEVEL_CRAP);
+  }
+}
+
+sub cmd_mailboxes {
+  my ($data, $server, $item) = @_;
+  if ($data =~ m/^[(show)|(add)|(del)]/i ) {
+    Irssi::command_runsub ('mailbox', $data, $server, $item);
+  }
+  else {
+    Irssi::print("Use /mailbox (show|add|del).")
+  }
+}
+
+sub init_mailboxes {
+  # Add the mailboxes at startup of the script
+  my $boxes = Irssi::settings_get_str('mail_mailboxes');
+  if (length($boxes) > 0) {
+    add_mailboxes($boxes);
+  }
+}
+
 sub read_settings {
   $extprog = Irssi::settings_get_str('mail_ext_program');
   my $time = Irssi::settings_get_int('mail_refresh_time');
-  return if ($time == $last_refresh_time);
+  my $mode = Irssi::settings_get_bool('maildir_mode');
+  unless ($time == $last_refresh_time) {
+     $last_refresh_time = $time;
+     Irssi::timeout_remove($refresh_tag) if ($refresh_tag);
+     $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_mail', undef);
+  }
+  return if ($mode == $last_mode);
+  $last_mode = $mode;
+  refresh_mail;
+}
+
 
-  $last_refresh_time = $time;
-  Irssi::timeout_remove($refresh_tag) if ($refresh_tag);
-  $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_mail', undef);
+if (!$maildirmode) {
+  my $default = "1=" . $ENV{'MAIL'} . ",";
+  Irssi::settings_add_str('misc', 'mail_mailboxes', $default);
+} else {
+  my $default = "1=~/Maildir/,";
+  Irssi::settings_add_str('misc', 'mail_mailboxes', $default);
 }
 
+Irssi::command_bind('mailbox show', 'cmd_showmailboxes');
+Irssi::command_bind('mailbox add', 'cmd_addmailbox');
+Irssi::command_bind('mailbox del', 'cmd_delmailbox');
+Irssi::command_bind('mailbox help', 'cmd_print_help');
+Irssi::command_bind('mailbox', 'cmd_mailboxes');
+
 Irssi::settings_add_str('misc', 'mail_ext_program', '');
-Irssi::settings_add_str('misc', 'mail_file', $ENV{'MAIL'});
 Irssi::settings_add_int('misc', 'mail_refresh_time', 60);
+Irssi::settings_add_bool('misc', 'maildir_mode', "$maildirmode");
+Irssi::settings_add_bool('misc', 'mail_oldnotnew', "$old_is_not_new");
+Irssi::settings_add_str('misc', 'mail_separator', ",");
+Irssi::settings_add_bool('misc', 'mail_show_message', "0");
+Irssi::settings_add_str('misc', 'mail_format', '%n:%u/%t');
+Irssi::settings_add_bool('misc', 'mail_show_only_unread', "1");
 
 Irssi::statusbar_item_register('mail', '{sb Mail: $0-}', 'mail');
 
 read_settings();
+init_mailboxes();
 Irssi::signal_add('setup changed', 'read_settings');
-mbox_count(Irssi::settings_get_str('mail_file'));
+refresh_mail();
+
+# EOF
diff --git a/apps/irssi/scripts/mlock.pl b/apps/irssi/scripts/mlock.pl
new file mode 100644 (file)
index 0000000..ed2fe52
--- /dev/null
@@ -0,0 +1,135 @@
+# /MLOCK <channel> <mode>
+#
+# Locks the channel mode to <mode>, if someone else tries to change the mode
+# Irssi will automatically change it back. +k and +l are a bit special since
+# they require the parameter. If you omit the parameter, like setting the
+# mode to "+ntlk", Irssi will allow all +k and +l (or -lk) mode changes.
+# You can remove the lock with /MODE #channel -
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'mlock',
+    description => 'Channel mode locking',
+    license     => 'Public Domain',
+    changed    => 'Sun Mar 10 23:18 EET 2002'
+);
+
+my %keep_channels;
+
+sub cmd_mlock {
+       my ($data, $server) = @_;
+       my ($channel, $mode) = split(/ /, $data, 2);
+
+       if ($mode eq "-") {
+               # remove checking
+               delete $keep_channels{$channel};
+       } else {
+               $keep_channels{$channel} = $mode;
+               mlock_check_mode($server, $channel);
+       }
+}
+
+sub mlock_check_mode {
+        my ($server, $channame) = @_;
+
+       my $channel = $server->channel_find($channame);
+       return if (!$channel || !$channel->{chanop});
+
+        my $keep_mode = $keep_channels{$channame};
+       return if (!$keep_mode);
+
+       # old channel mode
+       my ($oldmode, $oldkey, $oldlimit);
+       $oldmode = $channel->{mode};
+        $oldmode =~ s/^([^ ]*).*/\1/;
+       $oldkey = $channel->{key};
+       $oldlimit = $channel->{limit};
+
+       # get the new channel key/limit
+       my (@newmodes, $newkey, $limit);
+       @newmodes = split(/ /, $keep_mode); $keep_mode = $newmodes[0];
+       if ($keep_mode =~ /k/) {
+               if ($keep_mode =~ /k.*l/) {
+                        $newkey = $newmodes[1];
+                        $limit = $newmodes[2];
+               } elsif ($keep_mode =~ /l.*k/) {
+                       $limit = $newmodes[1];
+                        $newkey = $newmodes[2];
+               } else {
+                        $newkey = $newmodes[1];
+               }
+       } elsif ($keep_mode =~ /l/) {
+               $limit = $newmodes[1];
+       }
+
+       # check the differences
+       my %allmodes;
+       $keep_mode =~ s/^\+//;
+       for (my $n = 0; $n < length($keep_mode); $n++) {
+               my $modechar = substr($keep_mode, $n, 1);
+               $allmodes{$modechar} = '+';
+       }
+
+       for (my $n = 0; $n < length($oldmode); $n++) {
+               my $modechar = substr($oldmode, $n, 1);
+
+               if ($allmodes{$modechar} eq '+') {
+                       next if (($modechar eq "k" && $newkey ne $oldkey) ||
+                                ($modechar eq "l" && $limit != $oldlimit));
+                       delete $allmodes{$modechar};
+               } else {
+                       $allmodes{$modechar} = '-';
+               }
+       }
+
+       # create the mode change string
+       my ($modecmd, $extracmd);
+       foreach my $mode (keys %allmodes) {
+               Irssi::print("key = '$mode':".$allmodes{$mode});
+               if ($mode eq "k") {
+                       if ($allmodes{$mode} eq '+') {
+                               next if ($newkey eq "");
+                               if ($oldkey ne "") {
+                                       # we need to get rid of old key too
+                                       $modecmd .= "-k";
+                                       $extracmd .= " $oldkey";
+                               }
+                               $extracmd .= " $newkey";
+                       } else {
+                               $extracmd .= " $oldkey";
+                       }
+               }
+               if ($mode eq "l" && $allmodes{$mode} eq '+') {
+                       next if ($limit <= 0);
+                        $extracmd .= " $limit";
+               }
+               $modecmd .= $allmodes{$mode}.$mode;
+       }
+
+       if ($modecmd ne "") {
+               $channel->{server}->command("mode $channame $modecmd$extracmd");
+       }
+}
+
+sub mlock_mode_changed {
+       my ($server, $data) = @_;
+       my ($channel, $mode) = split(/ /, $data, 2);
+
+       mlock_check_mode($server, $channel);
+}
+
+sub mlock_synced {
+       my $channel = $_[0];
+
+       mlock_check_mode($channel->{server}, $channel->{name});
+}
+
+Irssi::command_bind('mlock', 'cmd_mlock');
+Irssi::signal_add_last("event mode", "mlock_mode_changed");
+Irssi::signal_add("channel synced", "mlock_synced");
diff --git a/apps/irssi/scripts/quitmsg.pl b/apps/irssi/scripts/quitmsg.pl
new file mode 100644 (file)
index 0000000..41bddaa
--- /dev/null
@@ -0,0 +1,45 @@
+# If quit message isn't given, quit with a random message
+# read from ~/.irssi/irssi.quit
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+use vars qw($VERSION %IRSSI);
+
+$VERSION = "1.00";
+%IRSSI = (
+    authors     => 'Timo Sirainen',
+    name        => 'quitmsg',
+    description => 'Random quit messages',
+    license     => 'Public Domain',
+    changed    => 'Sun Mar 10 23:18 EET 2002'
+);
+
+my $quitfile = glob "~/.irssi/irssi.quit";
+
+sub cmd_quit {
+       my ($data, $server, $channel) = @_;
+       return if ($data ne "");
+
+       open (f, $quitfile) || return;
+       my $lines = 0; while(<f>) { $lines++; };
+
+       my $line = int(rand($lines))+1;
+
+       my $quitmsg;
+       seek(f, 0, 0); $. = 0;
+       while(<f>) {
+               next if ($. != $line);
+
+               chomp;
+               $quitmsg = $_;
+               last;
+       }
+       close(f);
+
+       foreach my $server (Irssi::servers) {
+               $server->command("disconnect ".$server->{tag}." $quitmsg");
+       }
+}
+
+Irssi::command_bind('quit', 'cmd_quit');
diff --git a/apps/irssi/scripts/scriptassist.pl b/apps/irssi/scripts/scriptassist.pl
new file mode 100644 (file)
index 0000000..786ec57
--- /dev/null
@@ -0,0 +1,1160 @@
+# by Stefan "tommie" Tomanek
+#
+# scriptassist.pl
+
+
+use strict;
+
+use vars qw($VERSION %IRSSI);
+$VERSION = '2003020803';
+%IRSSI = (
+    authors     => 'Stefan \'tommie\' Tomanek',
+    contact     => 'stefan@pico.ruhr.de',
+    name        => 'scriptassist',
+    description => 'keeps your scripts on the cutting edge',
+    license     => 'GPLv2',
+    url         => 'http://irssi.org/scripts/',
+    changed     => $VERSION,
+    modules     => 'Data::Dumper LWP::UserAgent (GnuPG)',
+    commands   => "scriptassist"
+);
+
+use vars qw($forked %remote_db $have_gpg);
+
+use Irssi 20020324;
+use Data::Dumper;
+use LWP::UserAgent;
+use POSIX;
+
+# GnuPG is not always needed
+use vars qw($have_gpg @complist);
+$have_gpg = 0;
+eval "use GnuPG qw(:algo :trust);";
+$have_gpg = 1 if not ($@);
+
+sub show_help() {
+    my $help = "scriptassist $VERSION
+/scriptassist check
+    Check all loaded scripts for new available versions
+/scriptassist update <script|all>
+    Update the selected or all script to the newest version
+/scriptassist search <query>
+    Search the script database
+/scriptassist info <scripts>
+    Display information about <scripts>
+/scriptassist ratings <scripts>
+    Retrieve the average ratings of the the scripts
+/scriptassist top <num>
+    Retrieve the first <num> top rated scripts
+/scriptassist new <num>
+    Display the newest <num> scripts
+/scriptassist rate <script> <stars>
+    Rate the script with a number of stars ranging from 0-5
+/scriptassist contact <script>
+    Write an email to the author of the script
+    (Requires OpenURL)
+/scriptassist cpan <module>
+    Visit CPAN to look for missing Perl modules
+    (Requires OpenURL)
+/scriptassist install <script>
+    Retrieve and load the script
+/scriptassist autorun <script>
+    Toggles automatic loading of <script>
+";  
+    my $text='';
+    foreach (split(/\n/, $help)) {
+        $_ =~ s/^\/(.*)$/%9\/$1%9/;
+        $text .= $_."\n";
+    }
+    print CLIENTCRAP &draw_box("ScriptAssist", $text, "scriptassist help", 1);
+    #theme_box("ScriptAssist", $text, "scriptassist help", 1);
+}
+
+sub theme_box ($$$$) {
+    my ($title, $text, $footer, $colour) = @_;
+    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_header', $title);
+    foreach (split(/\n/, $text)) {
+       Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_inside', $_);
+    }
+    Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'box_footer', $footer);
+}
+
+sub draw_box ($$$$) {
+    my ($title, $text, $footer, $colour) = @_;
+    my $box = '';
+    $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n";
+    foreach (split(/\n/, $text)) {
+        $box .= '%R|%n '.$_."\n";
+    }                                                                               $box .= '%R`--<%n'.$footer.'%R>->%n';
+    $box =~ s/%.//g unless $colour;
+    return $box;
+}
+
+sub call_openurl ($) {
+    my ($url) = @_;
+    no strict "refs";
+    # check for a loaded openurl
+    if (defined %{ "Irssi::Script::openurl::" }) {
+        &{ "Irssi::Script::openurl::launch_url" }($url);
+    } else {
+        print CLIENTCRAP "%R>>%n Please install openurl.pl";
+    }
+    use strict;
+}
+
+sub bg_do ($) {
+    my ($func) = @_; 
+    my ($rh, $wh);
+    pipe($rh, $wh);
+    if ($forked) {
+       print CLIENTCRAP "%R>>%n Please wait until your earlier request has been finished.";
+       return;
+    }
+    my $pid = fork();
+    $forked = 1;
+    if ($pid > 0) {
+       print CLIENTCRAP "%R>>%n Please wait...";
+        close $wh;
+        Irssi::pidwait_add($pid);
+        my $pipetag;
+        my @args = ($rh, \$pipetag, $func);
+        $pipetag = Irssi::input_add(fileno($rh), INPUT_READ, \&pipe_input, \@args);
+    } else {
+       eval {
+           my @items = split(/ /, $func);
+           my %result;
+           my $ts1 = $remote_db{timestamp};
+           my $xml = get_scripts();
+           my $ts2 = $remote_db{timestamp};
+           if (not($ts1 eq $ts2) && Irssi::settings_get_bool('scriptassist_cache_sources')) {
+               $result{db} = $remote_db{db};
+               $result{timestamp} = $remote_db{timestamp};
+           }
+           if ($items[0] eq 'check') {
+               $result{data}{check} = check_scripts($xml);
+           } elsif ($items[0] eq 'update') {
+               shift(@items);
+               $result{data}{update} = update_scripts(\@items, $xml);
+           } elsif ($items[0] eq 'search') {
+               shift(@items);
+               #$result{data}{search}{-foo} = 0;
+               foreach (@items) {
+                   $result{data}{search}{$_} = search_scripts($_, $xml);
+               }
+           } elsif ($items[0] eq 'install') {
+               shift(@items);
+               $result{data}{install} = install_scripts(\@items, $xml);
+           } elsif ($items[0] eq 'debug') {
+               shift(@items);
+               $result{data}{debug} = debug_scripts(\@items);
+           } elsif ($items[0] eq 'ratings') {
+               shift(@items);
+               @items = @{ loaded_scripts() } if $items[0] eq "all";
+               #$result{data}{rating}{-foo} = 1;
+               my %ratings = %{ get_ratings(\@items, '') };
+               foreach (keys %ratings) {
+                   $result{data}{rating}{$_}{rating} = $ratings{$_}->[0];
+                   $result{data}{rating}{$_}{votes} = $ratings{$_}->[1];
+               }
+           } elsif ($items[0] eq 'rate') {
+               #$result{data}{rate}{-foo} = 1;
+               $result{data}{rate}{$items[1]} = rate_script($items[1], $items[2]);
+           } elsif ($items[0] eq 'info') {
+               shift(@items);
+               $result{data}{info} = script_info(\@items);
+           } elsif ($items[0] eq 'echo') {
+               $result{data}{echo} = 1;
+           } elsif ($items[0] eq 'top') {
+               my %ratings = %{ get_ratings([], $items[1]) };
+               foreach (keys %ratings) {
+                    $result{data}{rating}{$_}{rating} = $ratings{$_}->[0];
+                    $result{data}{rating}{$_}{votes} = $ratings{$_}->[1];
+                }
+           } elsif ($items[0] eq 'new') {
+               my $new = get_new($items[1]);
+               $result{data}{new} = $new;
+           } elsif ($items[0] eq 'unknown') {
+               my $cmd = $items[1];
+               $result{data}{unknown}{$cmd} = get_unknown($cmd, $xml);
+           }
+           my $dumper = Data::Dumper->new([\%result]);
+           $dumper->Purity(1)->Deepcopy(1)->Indent(0);
+           my $data = $dumper->Dump;
+           print($wh $data);
+       };
+       close($wh);
+       POSIX::_exit(1);
+    }
+}
+
+sub get_unknown ($$) {
+    my ($cmd, $db) = @_;
+    foreach (keys %$db) {
+       next unless defined $db->{$_}{commands};
+       foreach my $item (split / /, $db->{$_}{commands}) {
+           return { $_ => $db->{$_} } if ($item =~ /^$cmd$/i);
+       }
+    }
+    return undef;
+}
+
+sub script_info ($) {
+    my ($scripts) = @_;
+    no strict "refs";
+    my %result;
+    my $xml = get_scripts();
+    foreach (@{$scripts}) {
+       next unless (defined $xml->{$_.".pl"} || (defined %{ 'Irssi::Script::'.$_.'::' } && defined %{ 'Irssi::Script::'.$_.'::IRSSI' }));
+       $result{$_}{version} = get_remote_version($_, $xml);
+       my @headers = ('authors', 'contact', 'description', 'license', 'source');
+       foreach my $entry (@headers) {
+           $result{$_}{$entry} = ${ 'Irssi::Script::'.$_.'::IRSSI' }{$entry};
+           if (defined $xml->{$_.".pl"}{$entry}) {
+               $result{$_}{$entry} = $xml->{$_.".pl"}{$entry};
+           }
+       }
+       if ($xml->{$_.".pl"}{signature_available}) {
+           $result{$_}{signature_available} = 1;
+       }
+       if (defined $xml->{$_.".pl"}{modules}) {
+           my $modules = $xml->{$_.".pl"}{modules};
+           #$result{$_}{modules}{-foo} = 1;
+           foreach my $mod (split(/ /, $modules)) {
+               my $opt = ($mod =~ /\((.*)\)/)? 1 : 0;
+               $mod = $1 if $1;
+               $result{$_}{modules}{$mod}{optional} = $opt;
+               $result{$_}{modules}{$mod}{installed} = module_exist($mod);
+           }
+       } elsif (defined ${ 'Irssi::Script::'.$_.'::IRSSI' }{modules}) {
+           my $modules = ${ 'Irssi::Script::'.$_.'::IRSSI' }{modules};
+           foreach my $mod (split(/ /, $modules)) {
+               my $opt = ($mod =~ /\((.*)\)/)? 1 : 0;
+               $mod = $1 if $1;
+               $result{$_}{modules}{$mod}{optional} = $opt;
+               $result{$_}{modules}{$mod}{installed} = module_exist($mod);
+           }
+       }
+       if (defined $xml->{$_.".pl"}{depends}) {
+           my $depends = $xml->{$_.".pl"}{depends};
+           foreach my $dep (split(/ /, $depends)) {
+               $result{$_}{depends}{$dep}{installed} = 1; #(defined ${ 'Irssi::Script::'.$dep }); 
+           }
+       }
+    }
+    return \%result;
+}
+
+sub rate_script ($$) {
+    my ($script, $stars) = @_;
+    my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30);
+    $ua->agent('ScriptAssist/'.$VERSION);
+    my $request = HTTP::Request->new('GET', 'http://ratings.irssi.de/irssirate.pl?&stars='.$stars.'&mode=rate&script='.$script);
+    my $response = $ua->request($request);
+    unless ($response->is_success() && $response->content() =~ /You already rated this script/) {
+       return 1;
+    } else {
+       return 0;
+    }
+}
+
+sub get_ratings ($$) {
+    my ($scripts, $limit) = @_;
+    my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30);
+    $ua->agent('ScriptAssist/'.$VERSION);
+    my $script = join(',', @{$scripts});
+    my $request = HTTP::Request->new('GET', 'http://ratings.irssi.de/irssirate.pl?script='.$script.'&sort=rating&limit='.$limit);
+    my $response = $ua->request($request);
+    my %result;
+    if ($response->is_success()) {
+       foreach (split /\n/, $response->content()) {
+           if (/<tr><td><a href=".*?">(.*?)<\/a>/) {
+               my $entry = $1;
+               if (/"><\/td><td>([0-9.]+)<\/td><td>(.*?)<\/td><td>/) {
+                   $result{$entry} = [$1, $2];
+               }
+           }
+       }
+    }
+    return \%result;
+}
+
+sub get_new ($) {
+    my ($num) = @_;
+    my $result;
+    my $xml = get_scripts();
+    foreach (sort {$xml->{$b}{last_modified} cmp $xml->{$a}{last_modified}} keys %$xml) {
+       my %entry = %{ $xml->{$_} };
+       $result->{$_} = \%entry;
+       $num--;
+       last unless $num;
+    }
+    return $result;
+}
+sub module_exist ($) {
+    my ($module) = @_;
+    $module =~ s/::/\//g;
+    foreach (@INC) {
+       return 1 if (-e $_."/".$module.".pm");
+    }
+    return 0;
+}
+
+sub debug_scripts ($) {
+    my ($scripts) = @_;
+    my %result;
+    foreach (@{$scripts}) {
+       my $xml = get_scripts();
+       if (defined $xml->{$_.".pl"}{modules}) {
+           my $modules = $xml->{$_.".pl"}{modules};
+           foreach my $mod (split(/ /, $modules)) {
+                my $opt = ($mod =~ /\((.*)\)/)? 1 : 0;
+                $mod = $1 if $1;
+                $result{$_}{$mod}{optional} = $opt;
+                $result{$_}{$mod}{installed} = module_exist($mod);
+           }
+       }
+    }
+    return(\%result);
+}
+
+sub install_scripts ($$) {
+    my ($scripts, $xml) = @_;
+    my %success;
+    #$success{-foo} = 1;
+    my $dir = Irssi::get_irssi_dir()."/scripts/";
+    foreach (@{$scripts}) {
+       if (get_local_version($_) && (-e $dir.$_.".pl")) {
+           $success{$_}{installed} = -2;
+       } else {
+           $success{$_} = download_script($_, $xml);
+       }
+    }
+    return \%success;
+}
+
+sub update_scripts ($$) {
+    my ($list, $database) = @_;
+    $list = loaded_scripts() if ($list->[0] eq "all" || scalar(@$list) == 0);
+    my %status;
+    #$status{-foo} = 1;
+    foreach (@{$list}) {
+       my $local = get_local_version($_);
+       my $remote = get_remote_version($_, $database);
+       next if $local eq '' || $remote eq '';
+       if (compare_versions($local, $remote) eq "older") {
+           $status{$_} = download_script($_, $database);
+       } else {
+           $status{$_}{installed} = -2;
+       }
+       $status{$_}{remote} = $remote;
+       $status{$_}{local} = $local;
+    }
+    return \%status;
+}
+
+sub search_scripts ($$) {
+    my ($query, $database) = @_;
+    my %result;
+    #$result{-foo} = " ";
+    foreach (sort keys %{$database}) {
+       my %entry = %{$database->{$_}};
+       my $string = $_." ";
+       $string .= $entry{description} if defined $entry{description};
+       if ($string =~ /$query/i) {
+           my $name = $_;
+           $name =~ s/\.pl$//;
+           if (defined $entry{description}) {
+               $result{$name}{desc} = $entry{description};
+           } else {
+               $result{$name}{desc} = "";
+           }
+           if (defined $entry{authors}) {
+               $result{$name}{authors} = $entry{authors};
+           } else {
+               $result{$name}{authors} = "";
+           }
+           if (get_local_version($name)) {
+               $result{$name}{installed} = 1;
+           } else {
+               $result{$name}{installed} = 0;
+           }
+       }
+    }
+    return \%result;
+}
+
+sub pipe_input {
+    my ($rh, $pipetag) = @{$_[0]};
+    my @lines = <$rh>;
+    close($rh);
+    Irssi::input_remove($$pipetag);
+    $forked = 0;
+    my $text = join("", @lines);
+    unless ($text) {
+       print CLIENTCRAP "%R<<%n Something weird happend";
+       return();
+    }
+    no strict "vars";
+    my $incoming = eval("$text");
+    if ($incoming->{db} && $incoming->{timestamp}) {
+       $remote_db{db} = $incoming->{db};
+       $remote_db{timestamp} = $incoming->{timestamp};
+    }
+    unless (defined $incoming->{data}) {
+       print CLIENTCRAP "%R<<%n Something weird happend";
+       return;
+    }
+    my %result = %{ $incoming->{data} };
+    @complist = ();
+    if (defined $result{new}) {
+       print_new($result{new});
+       push @complist, $_ foreach keys %{ $result{new} };
+    }
+    if (defined $result{check}) {
+       print_check(%{$result{check}});
+       push @complist, $_ foreach keys %{ $result{check} };
+    }
+    if (defined $result{update}) {
+       print_update(%{ $result{update} });
+       push @complist, $_ foreach keys %{ $result{update} };
+    }
+    if (defined $result{search}) {
+       foreach (keys %{$result{search}}) {
+           print_search($_, %{$result{search}{$_}});
+           push @complist, keys(%{$result{search}{$_}});
+       }
+    }
+    if (defined $result{install}) {
+       print_install(%{ $result{install} });
+       push @complist, $_ foreach keys %{ $result{install} };
+    }
+    if (defined $result{debug}) {
+       print_debug(%{ $result{debug} });
+    }
+    if (defined $result{rating}) {
+       print_ratings(%{ $result{rating} });
+       push @complist, $_ foreach keys %{ $result{rating} };
+    }
+    if (defined $result{rate}) {
+       print_rate(%{ $result{rate} });
+    }
+    if (defined $result{info}) {
+       print_info(%{ $result{info} });
+    }
+    if (defined $result{echo}) {
+       Irssi::print "ECHO";
+    }
+    if ($result{unknown}) {
+        print_unknown($result{unknown});
+    }
+
+}
+
+sub print_unknown ($) {
+    my ($data) = @_;
+    foreach my $cmd (keys %$data) {
+       print CLIENTCRAP "%R<<%n No script provides '/$cmd'" unless $data->{$cmd};
+       foreach (keys %{ $data->{$cmd} }) {
+           my $text .= "The command '/".$cmd."' is provided by the script '".$data->{$cmd}{$_}{name}."'.\n";
+           $text .= "This script is currently not installed on your system.\n";
+           $text .= "If you want to install the script, enter\n";
+           my ($name) = /(.*?)\.pl$/;
+           $text .= "  %U/script install ".$name."%U ";
+           my $output = draw_box("ScriptAssist", $text, "'".$_."' missing", 1);
+           print CLIENTCRAP $output;
+       }
+    }
+}
+
+sub check_autorun ($) {
+    my ($script) = @_;
+    my $dir = Irssi::get_irssi_dir()."/scripts/";
+    if (-e $dir."/autorun/".$script.".pl") {
+       if (readlink($dir."/autorun/".$script.".pl") eq "../".$script.".pl") {
+           return 1;
+       }
+    }
+    return 0;
+}
+
+sub array2table {
+    my (@array) = @_;
+    my @width;
+    foreach my $line (@array) {
+        for (0..scalar(@$line)-1) {
+            my $l = $line->[$_];
+            $l =~ s/%[^%]//g;
+            $l =~ s/%%/%/g;
+            $width[$_] = length($l) if $width[$_]<length($l);
+        }
+    }   
+    my $text;
+    foreach my $line (@array) {
+        for (0..scalar(@$line)-1) {
+            my $l = $line->[$_];
+            $text .= $line->[$_];
+            $l =~ s/%[^%]//g;
+            $l =~ s/%%/%/g;
+            $text .= " "x($width[$_]-length($l)+1) unless ($_ == scalar(@$line)-1);
+        }
+        $text .= "\n";
+    }
+    return $text;
+}
+
+
+sub print_info (%) {
+    my (%data) = @_;
+    my $line;
+    foreach my $script (sort keys(%data)) {
+       my ($local, $autorun);
+       if (get_local_version($script)) {
+           $line .= "%go%n ";
+           $local = get_local_version($script);
+       } else {
+           $line .= "%ro%n ";
+           $local = undef;
+       }
+       if (defined $local || check_autorun($script)) {
+           $autorun = "no";
+           $autorun = "yes" if check_autorun($script);
+       } else {
+           $autorun = undef;
+       }
+       $line .= "%9".$script."%9\n";
+       $line .= "  Version    : ".$data{$script}{version}."\n";
+       $line .= "  Source     : ".$data{$script}{source}."\n";
+       $line .= "  Installed  : ".$local."\n" if defined $local;
+       $line .= "  Autorun    : ".$autorun."\n" if defined $autorun;
+       $line .= "  Authors    : ".$data{$script}{authors};
+       $line .= " %Go-m signed%n" if $data{$script}{signature_available};
+       $line .= "\n";
+       $line .= "  Contact    : ".$data{$script}{contact}."\n";
+       $line .= "  Description: ".$data{$script}{description}."\n";
+       $line .= "\n" if $data{$script}{modules};
+       $line .= "  Needed Perl modules:\n" if $data{$script}{modules};
+
+        foreach (sort keys %{$data{$script}{modules}}) {
+            if ( $data{$script}{modules}{$_}{installed} == 1 ) {
+                $line .= "  %g->%n ".$_." (found)";
+            } else {
+                $line .= "  %r->%n ".$_." (not found)";
+            }
+           $line .= " <optional>" if $data{$script}{modules}{$_}{optional};
+            $line .= "\n";
+        }
+       #$line .= "  Needed Irssi scripts:\n";
+       $line .= "  Needed Irssi Scripts:\n" if $data{$script}{depends};
+       foreach (sort keys %{$data{$script}{depends}}) {
+           if ( $data{$script}{depends}{$_}{installed} == 1 ) {
+               $line .= "  %g->%n ".$_." (loaded)";
+           } else {
+               $line .= "  %r->%n ".$_." (not loaded)";
+           }
+           #$line .= " <optional>" if $data{$script}{depends}{$_}{optional};
+           $line .= "\n";
+       }
+    }
+    print CLIENTCRAP draw_box('ScriptAssist', $line, 'info', 1) ;
+}
+
+sub print_rate (%) {
+    my (%data) = @_;
+    my $line;
+    foreach my $script (sort keys(%data)) {
+       if ($data{$script}) {
+            $line .= "%go%n %9".$script."%9 has been rated";
+        } else {
+            $line .= "%ro%n %9".$script."%9 : Already rated this script";
+        }
+    }
+    print CLIENTCRAP draw_box('ScriptAssist', $line, 'rating', 1) ;
+}
+
+sub print_ratings (%) {
+    my (%data) = @_;
+    my @table;
+    foreach my $script (sort {$data{$b}{rating}<=>$data{$a}{rating}} keys(%data)) {
+       my @line;
+       if (get_local_version($script)) {
+           push @line, "%go%n";
+       } else {
+           push @line, "%yo%n";
+       }
+        push @line, "%9".$script."%9";
+       push @line, $data{$script}{rating};
+       push @line, "[".$data{$script}{votes}." votes]";
+       push @table, \@line;
+    }
+    print CLIENTCRAP draw_box('ScriptAssist', array2table(@table), 'ratings', 1) ;
+}
+
+sub print_new ($) {
+    my ($list) = @_;
+    my @table;
+    foreach (sort {$list->{$b}{last_modified} cmp $list->{$a}{last_modified}} keys %$list) {
+       my @line;
+       my ($name) = /^(.*?)\.pl$/;
+        if (get_local_version($name)) {
+            push @line, "%go%n";
+        } else {
+            push @line, "%yo%n";
+        }
+       push @line, "%9".$name."%9";
+       push @line, $list->{$_}{last_modified};
+       push @table, \@line;
+    }
+    print CLIENTCRAP draw_box('ScriptAssist', array2table(@table), 'new scripts', 1) ;
+}
+
+sub print_debug (%) {
+    my (%data) = @_;
+    my $line;
+    foreach my $script (sort keys %data) {
+       $line .= "%ro%n %9".$script."%9 failed to load\n";
+       $line .= "  Make sure you have the following perl modules installed:\n";
+       foreach (sort keys %{$data{$script}}) {
+           if ( $data{$script}{$_}{installed} == 1 ) {
+               $line .= "  %g->%n ".$_." (found)";
+           } else {
+               $line .= "  %r->%n ".$_." (not found)\n";
+               $line .= "     [This module is optional]\n" if $data{$script}{$_}{optional};
+               $line .= "     [Try /scriptassist cpan ".$_."]";
+           }
+           $line .= "\n";
+       }
+       print CLIENTCRAP draw_box('ScriptAssist', $line, 'debug', 1) ;
+    }
+}
+
+sub load_script ($) {
+    my ($script) = @_;
+    Irssi::command('script load '.$script);
+}
+
+sub print_install (%) {
+    my (%data) = @_;
+    my $text;
+    my ($crashed, @installed);
+    foreach my $script (sort keys %data) {
+       my $line;
+       if ($data{$script}{installed} == 1) {
+           my $hacked;
+           if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) {
+               if ($data{$script}{signed} >= 0) {
+                   load_script($script) unless (lc($script) eq lc($IRSSI{name}));
+               } else {
+                   $hacked = 1;
+               }
+           } else {
+               load_script($script) unless (lc($script) eq lc($IRSSI{name}));
+           }
+           if (get_local_version($script) && not lc($script) eq lc($IRSSI{name})) {
+               $line .= "%go%n %9".$script."%9 installed\n";
+               push @installed, $script;
+           } elsif (lc($script) eq lc($IRSSI{name})) {
+               $line .= "%yo%n %9".$script."%9 installed, please reload manually\n";
+           } else {
+               $line .= "%Ro%n %9".$script."%9 fetched, but unable to load\n";
+               $crashed .= $script." " unless $hacked;
+           }
+           if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) {
+               foreach (split /\n/, check_sig($data{$script})) {
+                   $line .= "  ".$_."\n";
+               }
+           }
+       } elsif ($data{$script}{installed} == -2) {
+           $line .= "%ro%n %9".$script."%9 already loaded, please try \"update\"\n";
+       } elsif ($data{$script}{installed} <= 0) {
+           $line .= "%ro%n %9".$script."%9 not installed\n";
+           foreach (split /\n/, check_sig($data{$script})) {
+               $line .= "  ".$_."\n";
+           }
+       } else {
+           $line .= "%Ro%n %9".$script."%9 not found on server\n";
+       }
+       $text .= $line;
+    }
+    # Inspect crashed scripts
+    bg_do("debug ".$crashed) if $crashed;
+    print CLIENTCRAP draw_box('ScriptAssist', $text, 'install', 1);
+    list_sbitems(\@installed);
+}
+
+sub list_sbitems ($) {
+    my ($scripts) = @_;
+    my $text;
+    foreach (@$scripts) {
+       no strict 'refs';
+       next unless defined %{ "Irssi::Script::${_}::" };
+       next unless defined %{ "Irssi::Script::${_}::IRSSI" };
+       my %header = %{ "Irssi::Script::${_}::IRSSI" };
+       next unless $header{sbitems};
+       $text .= '%9"'.$_.'"%9 provides the following statusbar item(s):'."\n";
+       $text .= '  ->'.$_."\n" foreach (split / /, $header{sbitems});
+    }
+    return unless $text;
+    $text .= "\n";
+    $text .= "Enter '/statusbar window add <item>' to add an item.";
+    print CLIENTCRAP draw_box('ScriptAssist', $text, 'sbitems', 1);
+}
+
+sub check_sig ($) {
+    my ($sig) = @_;
+    my $line;
+    my %trust = ( -1 => 'undefined',
+                   0 => 'never',
+                  1 => 'marginal',
+                  2 => 'fully',
+                  3 => 'ultimate'
+                );
+    if ($sig->{signed} == 1) {
+       $line .= "Signature found from ".$sig->{sig}{user}."\n";
+       $line .= "Timestamp  : ".$sig->{sig}{date}."\n";
+       $line .= "Fingerprint: ".$sig->{sig}{fingerprint}."\n";
+       $line .= "KeyID      : ".$sig->{sig}{keyid}."\n";
+       $line .= "Trust      : ".$trust{$sig->{sig}{trust}}."\n";
+    } elsif ($sig->{signed} == -1) {
+       $line .= "%1Warning, unable to verify signature%n\n";
+    } elsif ($sig->{signed} == 0) {
+       $line .= "%1No signature found%n\n" unless Irssi::settings_get_bool('scriptassist_install_unsigned_scripts');
+    }
+    return $line;
+}
+
+sub print_search ($%) {
+    my ($query, %data) = @_;
+    my $text;
+    foreach (sort keys %data) {
+       my $line;
+       $line .= "%go%n" if $data{$_}{installed};
+       $line .= "%yo%n" if not $data{$_}{installed};
+       $line .= " %9".$_."%9 ";
+       $line .= $data{$_}{desc};
+       $line =~ s/($query)/%U$1%U/gi;
+       $line .= ' ('.$data{$_}{authors}.')';
+       $text .= $line." \n";
+    }
+    print CLIENTCRAP draw_box('ScriptAssist', $text, 'search: '.$query, 1) ;
+}
+
+sub print_update (%) { 
+    my (%data) = @_;
+    my $text;
+    my @table;
+    my $verbose = Irssi::settings_get_bool('scriptassist_update_verbose');
+    foreach (sort keys %data) {
+       my $signed = 0;
+       if ($data{$_}{installed} == 1) {
+           my $local = $data{$_}{local};
+           my $remote = $data{$_}{remote};
+           push @table, ['%yo%n', '%9'.$_.'%9', 'upgraded ('.$local.'->'.$remote.')'];
+           foreach (split /\n/, check_sig($data{$_})) {
+               push @table, ['', '', $_];
+           }
+           if (lc($_) eq lc($IRSSI{name})) {
+               push @table, ['', '', "%R%9Please reload manually%9%n"];
+           } else {
+               load_script($_);
+           }
+       } elsif ($data{$_}{installed} == 0 || $data{$_}{installed} == -1) {
+           push @table, ['%yo%n', '%9'.$_.'%9', 'not upgraded'];
+            foreach (split /\n/, check_sig($data{$_})) {
+               push @table, ['', '', $_];
+            } 
+       } elsif ($data{$_}{installed} == -2 && $verbose) {
+           my $local = $data{$_}{local};
+           push @table, ['%go%n', '%9'.$_.'%9', 'already at the latest version ('.$local.')'];
+       }
+    }
+    $text = array2table(@table);
+    print CLIENTCRAP draw_box('ScriptAssist', $text, 'update', 1) ;
+}
+
+sub contact_author ($) {
+    my ($script) = @_;
+    no strict 'refs';
+    return unless defined %{ "Irssi::Script::${script}::" };
+    my %header = %{ "Irssi::Script::${script}::IRSSI" };
+    if (defined $header{contact}) {
+       my @ads = split(/ |,/, $header{contact});
+       my $address = $ads[0];
+       $address .= '?subject='.$script;
+       $address .= '_'.get_local_version($script) if defined get_local_version($script);
+       call_openurl($address);
+    }
+}
+
+sub get_scripts {
+    my $ua = LWP::UserAgent->new(env_proxy=>1, keep_alive=>1, timeout=>30);
+    $ua->agent('ScriptAssist/'.$VERSION);
+    $ua->env_proxy();
+    my @mirrors = split(/ /, Irssi::settings_get_str('scriptassist_script_sources'));
+    my %sites_db;
+    my $fetched = 0;
+    my @sources;
+    foreach my $site (@mirrors) {
+       my $request = HTTP::Request->new('GET', $site);
+       if ($remote_db{timestamp}) {
+           $request->if_modified_since($remote_db{timestamp});
+       }
+       my $response = $ua->request($request);
+       next unless $response->is_success;
+       $fetched = 1;
+       my $data = $response->content();
+       my ($src, $type);
+       if ($site =~ /(.*\/).+\.(.+)/) {
+           $src = $1;
+           $type = $2;
+       }
+       push @sources, $src;
+       #my @header = ('name', 'contact', 'authors', 'description', 'version', 'modules', 'last_modified');
+       if ($type eq 'dmp') {
+           no strict 'vars';
+           my $new_db = eval "$data";
+           foreach (keys %$new_db) {
+               if (defined $sites_db{script}{$_}) {
+                   my $old = $sites_db{$_}{version};
+                   my $new = $new_db->{$_}{version};
+                   next if (compare_versions($old, $new) eq 'newer');
+               }
+               #foreach my $key (@header) {
+               foreach my $key (keys %{ $new_db->{$_} }) {
+                   next unless defined $new_db->{$_}{$key};
+                   $sites_db{$_}{$key} = $new_db->{$_}{$key};
+               }
+               $sites_db{$_}{source} = $src;
+           }
+       } else {
+           ## FIXME Panic?!
+       }
+       
+    }
+    if ($fetched) {
+       # Clean database
+       foreach (keys %{$remote_db{db}}) {
+           foreach my $site (@sources) {
+               if ($remote_db{db}{$_}{source} eq $site) {
+                   delete $remote_db{db}{$_};
+                   last;
+               }
+           }
+       }
+       $remote_db{db}{$_} = $sites_db{$_} foreach (keys %sites_db);
+       $remote_db{timestamp} = time();
+    }
+    return $remote_db{db};
+}
+
+sub get_remote_version ($$) {
+    my ($script, $database) = @_;
+    return $database->{$script.".pl"}{version};
+}
+
+sub get_local_version ($) {
+    my ($script) = @_;
+    no strict 'refs';
+    return unless defined %{ "Irssi::Script::${script}::" };
+    my $version = ${ "Irssi::Script::${script}::VERSION" };
+    return $version;
+}
+
+sub compare_versions ($$) {
+    my ($ver1, $ver2) = @_;
+    my @ver1 = split /\./, $ver1;
+    my @ver2 = split /\./, $ver2;
+    #if (scalar(@ver2) != scalar(@ver1)) {
+    #    return 0;
+    #}       
+    my $cmp = 0;
+    ### Special thanks to Clemens Heidinger
+    $cmp ||= $ver1[$_] <=> $ver2[$_] || $ver1[$_] cmp $ver2[$_] for 0..scalar(@ver2);
+    return 'newer' if $cmp == 1;
+    return 'older' if $cmp == -1;
+    return 'equal';
+}
+
+sub loaded_scripts {
+    no strict 'refs';
+    my @modules;
+    foreach (sort grep(s/::$//, keys %Irssi::Script::)) {
+        #my $name    = ${ "Irssi::Script::${_}::IRSSI" }{name};
+        #my $version = ${ "Irssi::Script::${_}::VERSION" };
+       push @modules, $_;# if $name && $version;
+    }
+    return \@modules;
+
+}
+
+sub check_scripts {
+    my ($data) = @_;
+    my %versions;
+    #$versions{-foo} = 1;
+    foreach (@{loaded_scripts()}) {
+        my $remote = get_remote_version($_, $data);
+        my $local =  get_local_version($_);
+       my $state;
+       if ($local && $remote) {
+           $state = compare_versions($local, $remote);
+       } elsif ($local) {
+           $state = 'noversion';
+           $remote = '/';
+       } else {
+           $state = 'noheader';
+           $local = '/';
+           $remote = '/';
+       }
+       if ($state) {
+           $versions{$_}{state} = $state;
+           $versions{$_}{remote} = $remote;
+           $versions{$_}{local} = $local;
+       }
+    }
+    return \%versions;
+}
+
+sub download_script ($$) {
+    my ($script, $xml) = @_;
+    my %result;
+    my $site = $xml->{$script.".pl"}{source};
+    $result{installed} = 0;
+    $result{signed} = 0;
+    my $dir = Irssi::get_irssi_dir();
+    my $ua = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30);
+    $ua->agent('ScriptAssist/'.$VERSION);
+    my $request = HTTP::Request->new('GET', $site.'/scripts/'.$script.'.pl');
+    my $response = $ua->request($request);
+    if ($response->is_success()) {
+       my $file = $response->content();
+       mkdir $dir.'/scripts/' unless (-e $dir.'/scripts/');
+       local *F;
+       open(F, '>'.$dir.'/scripts/'.$script.'.pl.new');
+       print F $file;
+       close(F);
+       if ($have_gpg && Irssi::settings_get_bool('scriptassist_use_gpg')) {
+           my $ua2 = LWP::UserAgent->new(env_proxy => 1,keep_alive => 1,timeout => 30);
+           $ua->agent('ScriptAssist/'.$VERSION);
+           my $request2 = HTTP::Request->new('GET', $site.'/signatures/'.$script.'.pl.asc');
+           my $response2 = $ua->request($request2);
+           if ($response2->is_success()) {
+               local *S;
+               my $sig_dir = $dir.'/scripts/signatures/';
+               mkdir $sig_dir unless (-e $sig_dir);
+               open(S, '>'.$sig_dir.$script.'.pl.asc');
+               my $file2 = $response2->content();
+               print S $file2;
+               close(S);
+               my $sig;
+               foreach (1..2) {
+                   # FIXME gpg needs two rounds to load the key
+                   my $gpg = new GnuPG();
+                   eval {
+                       $sig = $gpg->verify( file => $dir.'/scripts/'.$script.'.pl.new', signature => $sig_dir.$script.'.pl.asc' );
+                   };
+               }
+               if (defined $sig->{user}) {
+                   $result{installed} = 1;
+                   $result{signed} = 1;
+                   $result{sig}{$_} = $sig->{$_} foreach (keys %{$sig});
+               } else {
+                   # Signature broken?
+                   $result{installed} = 0;
+                   $result{signed} = -1;
+               }
+           } else {
+               $result{signed} = 0;
+               $result{installed} = -1;
+               $result{installed} = 1 if Irssi::settings_get_bool('scriptassist_install_unsigned_scripts');
+           }
+       } else {
+           $result{signed} = 0;
+           $result{installed} = -1;
+           $result{installed} = 1 if Irssi::settings_get_bool('scriptassist_install_unsigned_scripts');
+       }
+    }
+    if ($result{installed}) {
+       my $old_dir = "$dir/scripts/old/";
+       mkdir $old_dir unless (-e $old_dir);
+       rename "$dir/scripts/$script.pl", "$old_dir/$script.pl.old" if -e "$dir/scripts/$script.pl";
+       rename "$dir/scripts/$script.pl.new", "$dir/scripts/$script.pl";
+    }
+    return \%result;
+}
+
+sub print_check (%) {
+    my (%data) = @_;
+    my $text;
+    my @table;
+    foreach (sort keys %data) {
+       my $state = $data{$_}{state};
+       my $remote = $data{$_}{remote};
+       my $local = $data{$_}{local};
+       if (Irssi::settings_get_bool('scriptassist_check_verbose')) {
+           push @table, ['%go%n', '%9'.$_.'%9', 'Up to date. ('.$local.')'] if $state eq 'equal';
+       }
+       push @table, ['%mo%n', '%9'.$_.'%9', "No version information available on network."] if $state eq "noversion";
+       push @table, ['%mo%n', '%9'.$_.'%9', 'No header in script.'] if $state eq "noheader";
+       push @table, ['%bo%n', '%9'.$_.'%9', "Your version is newer (".$local."->".$remote.")"] if $state eq "newer";
+       push @table, ['%ro%n', '%9'.$_.'%9', "A new version is available (".$local."->".$remote.")"] if $state eq "older";;
+    }
+    $text = array2table(@table);
+    print CLIENTCRAP draw_box('ScriptAssist', $text, 'check', 1) ;
+}
+
+sub toggle_autorun ($) {
+    my ($script) = @_;
+    my $dir = Irssi::get_irssi_dir()."/scripts/";
+    mkdir $dir."autorun/" unless (-e $dir."autorun/");
+    return unless (-e $dir.$script.".pl");
+    if (check_autorun($script)) {
+       if (readlink($dir."/autorun/".$script.".pl") eq "../".$script.".pl") {
+           if (unlink($dir."/autorun/".$script.".pl")) {
+               print CLIENTCRAP "%R>>%n Autorun of ".$script." disabled";
+           } else {
+               print CLIENTCRAP "%R>>%n Unable to delete link";
+           }
+       } else {
+           print CLIENTCRAP "%R>>%n ".$dir."/autorun/".$script.".pl is not a correct link";
+       }
+    } else {
+       symlink("../".$script.".pl", $dir."/autorun/".$script.".pl");
+       print CLIENTCRAP "%R>>%n Autorun of ".$script." enabled";
+    }
+}
+
+sub sig_script_error ($$) {
+    my ($script, $msg) = @_;
+    return unless Irssi::settings_get_bool('scriptassist_catch_script_errors');
+    if ($msg =~ /Can't locate (.*?)\.pm in \@INC \(\@INC contains:(.*?) at/) {
+        my $module = $1;
+        $module =~ s/\//::/g;
+       missing_module($module);
+    }
+}
+
+sub missing_module ($$) {
+    my ($module) = @_;
+    my $text;
+    $text .= "The perl module %9".$module."%9 is missing on your system.\n";
+    $text .= "Please ask your administrator about it.\n";
+    $text .= "You can also check CPAN via '/scriptassist cpan ".$module."'.\n";
+    print CLIENTCRAP &draw_box('ScriptAssist', $text, $module, 1);
+}
+
+sub cmd_scripassist ($$$) {
+    my ($arg, $server, $witem) = @_;
+    my @args = split(/ /, $arg);
+    if ($args[0] eq 'help' || $args[0] eq '-h') {
+       show_help();
+    } elsif ($args[0] eq 'check') {
+       bg_do("check");
+    } elsif ($args[0] eq 'update') {
+       shift @args;
+       bg_do("update ".join(' ', @args));
+    } elsif ($args[0] eq 'search' && defined $args[1]) {
+       shift @args;
+       bg_do("search ".join(" ", @args));
+    } elsif ($args[0] eq 'install' && defined $args[1]) {
+       shift @args;
+       bg_do("install ".join(' ', @args));
+    } elsif ($args[0] eq 'contact' && defined $args[1]) {
+       contact_author($args[1]);
+    } elsif ($args[0] eq 'ratings' && defined $args[1]) {
+       shift @args;
+       bg_do("ratings ".join(' ', @args));
+    } elsif ($args[0] eq 'rate' && defined $args[1] && defined $args[2]) {
+       shift @args;
+       bg_do("rate ".join(' ', @args)) if ($args[2] >= 0 && $args[2] < 6);
+    } elsif ($args[0] eq 'info' && defined $args[1]) {
+       shift @args;
+       bg_do("info ".join(' ', @args));
+    } elsif ($args[0] eq 'echo') {
+       bg_do("echo");
+    } elsif ($args[0] eq 'top') {
+       my $number = defined $args[1] ? $args[1] : 10;
+       bg_do("top ".$number);
+    } elsif ($args[0] eq 'cpan' && defined $args[1]) {
+       call_openurl('http://search.cpan.org/search?mode=module&query='.$args[1]);
+    } elsif ($args[0] eq 'autorun' && defined $args[1]) {
+       toggle_autorun($args[1]);
+    } elsif ($args[0] eq 'new') {
+       my $number = defined $args[1] ? $args[1] : 5;
+       bg_do("new ".$number);
+    }
+}
+
+sub sig_command_script_load ($$$) {
+    my ($script, $server, $witem) = @_;
+    no strict;
+    $script = $2 if $script =~ /(.*\/)?(.*?)\.pl$/;
+    if (defined %{ "Irssi::Script::${script}::" }) {
+       if (defined &{ "Irssi::Script::${script}::pre_unload" }) {
+           print CLIENTCRAP "%R>>%n Triggering pre_unload function of $script...";
+           &{ "Irssi::Script::${script}::pre_unload" }();
+       }
+    }
+}
+
+sub sig_default_command ($$) {
+    my ($cmd, $server) = @_;
+    return unless Irssi::settings_get_bool("scriptassist_check_unknown_commands");
+    bg_do('unknown '.$cmd);
+}
+
+sub sig_complete ($$$$$) {
+    my ($list, $window, $word, $linestart, $want_space) = @_;
+    return unless $linestart =~ /^.script(assist)? (install|rate|ratings|update|check|contact|info|autorun)/;
+    my @newlist;
+    my $str = $word;
+    foreach (@complist) {
+       if ($_ =~ /^(\Q$str\E.*)?$/) {
+           push @newlist, $_;
+       }
+    }
+    foreach (@{loaded_scripts()}) {
+       push @newlist, $_ if /^(\Q$str\E.*)?$/;
+    }
+    $want_space = 0;
+    push @$list, $_ foreach @newlist;
+    Irssi::signal_stop();
+}
+
+
+Irssi::settings_add_str($IRSSI{name}, 'scriptassist_script_sources', 'http://scripts.irssi.org/scripts.dmp');
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_cache_sources', 1);
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_update_verbose', 1);
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_check_verbose', 1);
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_catch_script_errors', 1);
+
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_install_unsigned_scripts', 1);
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_use_gpg', 1);
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_integrate', 1);
+Irssi::settings_add_bool($IRSSI{name}, 'scriptassist_check_unknown_commands', 1);
+
+Irssi::signal_add_first("default command", \&sig_default_command);
+Irssi::signal_add_first('complete word', \&sig_complete);
+Irssi::signal_add_first('command script load', \&sig_command_script_load);
+Irssi::signal_add_first('command script unload', \&sig_command_script_load);
+
+if (defined &Irssi::signal_register) {
+    Irssi::signal_register({ 'script error' => [ 'Irssi::Script', 'string' ] });
+    Irssi::signal_add_last('script error', \&sig_script_error);
+}
+
+Irssi::command_bind('scriptassist', \&cmd_scripassist);
+
+Irssi::theme_register(['box_header', '%R,--[%n$*%R]%n',
+'box_inside', '%R|%n $*',
+'box_footer', '%R`--<%n$*%R>->%n',
+]);
+
+foreach my $cmd ( ( 'check', 'install', 'update', 'contact', 'search', '-h', 'help', 'ratings', 'rate', 'info', 'echo', 'top', 'cpan', 'autorun', 'new') ) {
+    Irssi::command_bind('scriptassist '.$cmd => sub {
+                       cmd_scripassist("$cmd ".$_[0], $_[1], $_[2]); });
+    if (Irssi::settings_get_bool('scriptassist_integrate')) {
+       Irssi::command_bind('script '.$cmd => sub {
+                           cmd_scripassist("$cmd ".$_[0], $_[1], $_[2]); });
+    }
+}
+
+print CLIENTCRAP '%B>>%n '.$IRSSI{name}.' '.$VERSION.' loaded: /scriptassist help for help';
diff --git a/apps/irssi/scripts/splitlong.pl b/apps/irssi/scripts/splitlong.pl
new file mode 100644 (file)
index 0000000..e88840b
--- /dev/null
@@ -0,0 +1,60 @@
+# /set splitlong_max_length
+#   specifies the maximum length of a msg, automatically chosen when set to "0"
+#   default: 0
+#
+# /set splitlong_line_start
+# /set splitlong_line_end
+#   self-explanatory
+#   defaults: "... ", " ..."
+###
+use strict;
+use vars qw($VERSION %IRSSI);
+
+use Irssi 20011001;
+
+$VERSION = "0.20";
+%IRSSI = (
+       authors     => "Bjoern \'fuchs\' Krombholz",
+       contact     => "bjkro\@gmx.de",
+       name        => "splitlong",
+       licence     => "Public Domain",
+       description => "Split overlong PRIVMSGs to msgs with length allowed by ircd",
+       changed     => "Wed Jun 25 00:17:00 CET 2003",
+       changes     => "Actually the real 0.19 (now 0.20), but upload didn't work some month ago, target problem fixed..."
+);
+
+sub sig_command_msg {
+       my ($cmd, $server, $winitem) = @_;
+       my ( $param, $target,$data) = $cmd =~ /^(-\S*\s)?(\S*)\s(.*)/;
+    
+       my $maxlength = Irssi::settings_get_int('splitlong_max_length');
+       my $lstart    = Irssi::settings_get_str('splitlong_line_start');
+       my $lend      = Irssi::settings_get_str('splitlong_line_end');
+
+       if ($maxlength == 0) {
+               # 497 = 510 - length(":" . "!" . " PRIVMSG " . " :");
+               $maxlength = 497 - length($server->{nick} . $server->{userhost} . $target);
+       }
+       my $maxlength2 = $maxlength - length($lend);
+
+       if (length($data) > ($maxlength)) {
+               my @spltarr;
+
+               while (length($data) > ($maxlength2)) {
+                       my $pos = rindex($data, " ", $maxlength2);
+                       push @spltarr, substr($data, 0, ($pos < ($maxlength/10 + 4)) ? $maxlength2  : $pos)  . $lend;
+                       $data = $lstart . substr($data, ($pos < ($maxlength/10 + 4)) ? $maxlength2 : $pos+1);
+               }
+
+               push @spltarr, $data;
+               foreach (@spltarr) {
+                       Irssi::signal_emit("command msg", "$target $_", $server, $winitem);
+               }
+               Irssi::signal_stop();
+       }
+}
+
+Irssi::settings_add_int('misc', 'splitlong_max_length', 0);
+Irssi::settings_add_str('misc', 'splitlong_line_start', "... ");
+Irssi::settings_add_str('misc', 'splitlong_line_end', " ...");
+Irssi::command_bind('msg', 'sig_command_msg');
diff --git a/apps/irssi/scripts/usercount.pl b/apps/irssi/scripts/usercount.pl
new file mode 100644 (file)
index 0000000..8bbc9e0
--- /dev/null
@@ -0,0 +1,172 @@
+use Irssi 20020101.0250 ();
+$VERSION = "1.16";
+%IRSSI = (
+    authors     => 'David Leadbeater, Timo Sirainen, Georg Lukas',
+    contact     => 'dgl@dgl.cx, tss@iki.fi, georg@boerde.de',
+    name        => 'usercount',
+    description => 'Adds a usercount for a channel as a statusbar item',
+    license     => 'GNU GPLv2 or later',
+    url         => 'http://irssi.dgl.yi.org/',
+);
+
+# Once you have loaded this script run the following command:
+# /statusbar window add usercount
+# You can also add -alignment left|right option
+
+# /set usercount_show_zero on or off to show users when 0 users of that type
+# /set usercount_show_ircops (default off)
+# /set usercount_show_halfops (default on)
+
+# you can customize the look of this item from theme file:
+#  sb_usercount = "{sb %_$0%_ nicks ($1-)}";
+#  sb_uc_ircops = "%_*%_$*";
+#  sb_uc_ops = "%_@%_$*";
+#  sb_uc_halfops = "%_%%%_$*";
+#  sb_uc_voices = "%_+%_$*";
+#  sb_uc_normal = "$*";
+#  sb_uc_space = " ";
+
+
+use strict;
+use Irssi::TextUI;
+
+my ($ircops, $ops, $halfops, $voices, $normal, $total);
+my ($timeout_tag, $recalc);
+
+# Called to make the status bar item
+sub usercount {
+  my ($item, $get_size_only) = @_;
+  my $wi = !Irssi::active_win() ? undef : Irssi::active_win()->{active};
+
+  if(!ref $wi || $wi->{type} ne "CHANNEL") { # only works on channels
+    return unless ref $item;
+    $item->{min_size} = $item->{max_size} = 0;
+    return;
+  }
+
+  if ($recalc) {
+    $recalc = 0;
+    calc_users($wi);
+  }
+
+  my $theme = Irssi::current_theme();
+  my $format = $theme->format_expand("{sb_usercount}");
+  if ($format) {
+    # use theme-specific look
+    my $ircopstr = $theme->format_expand("{sb_uc_ircops $ircops}",
+          Irssi::EXPAND_FLAG_IGNORE_EMPTY);
+    my $opstr = $theme->format_expand("{sb_uc_ops $ops}",
+          Irssi::EXPAND_FLAG_IGNORE_EMPTY);
+    my $halfopstr = $theme->format_expand("{sb_uc_halfops $halfops}",
+          Irssi::EXPAND_FLAG_IGNORE_EMPTY);
+    my $voicestr = $theme->format_expand("{sb_uc_voices $voices}", 
+          Irssi::EXPAND_FLAG_IGNORE_EMPTY);
+    my $normalstr = $theme->format_expand("{sb_uc_normal $normal}",
+          Irssi::EXPAND_FLAG_IGNORE_EMPTY);
+       my $space = $theme->format_expand('{sb_uc_space}',
+         Irssi::EXPAND_FLAG_IGNORE_EMPTY);
+       $space = " " unless $space;
+
+    my $str = "";
+    $str .= $ircopstr.$space if defined $ircops;
+    $str .= $opstr.$space  if defined $ops;
+    $str .= $halfopstr.$space if defined $halfops;
+    $str .= $voicestr.$space if defined $voices;
+    $str .= $normalstr.$space if defined $normal;
+    $str =~ s/\Q$space\E$//;
+
+    $format = $theme->format_expand("{sb_usercount $total $str}",
+                                   Irssi::EXPAND_FLAG_IGNORE_REPLACES);
+  } else {
+    # use the default look
+    $format = "{sb \%_$total\%_ nicks \%c(\%n";
+    $format .= '*'.$ircops.' ' if (defined $ircops);
+    $format .= '@'.$ops.' ' if (defined $ops);
+    $format .= '%%'.$halfops.' ' if (defined $halfops);
+    $format .= "+$voices " if (defined $voices);
+    $format .= "$normal " if (defined $normal);
+    $format =~ s/ $//;
+    $format .= "\%c)}";
+  }
+
+  $item->default_handler($get_size_only, $format, undef, 1);
+}
+
+sub calc_users() {
+  my $channel = shift;
+  my $server = $channel->{server};
+
+  $ircops = $ops = $halfops = $voices = $normal = 0;
+  for ($channel->nicks()) {
+    if ($_->{serverop}) {
+      $ircops++;
+       }
+
+    if ($_->{op}) {
+      $ops++;
+       } elsif ($_->{halfop}) {
+          $halfops++;
+    } elsif ($_->{voice}) {
+      $voices++;
+    } else {
+      $normal++;
+    }
+  }
+
+  $total = $ops+$halfops+$voices+$normal;
+  if (!Irssi::settings_get_bool('usercount_show_zero')) {
+    $ircops = undef if ($ircops == 0);
+    $ops = undef if ($ops == 0);
+    $halfops = undef if ($halfops == 0);
+    $voices = undef if ($voices == 0);
+    $normal = undef if ($normal == 0);
+  }
+  $halfops = undef unless Irssi::settings_get_bool('usercount_show_halfops');
+  $ircops = undef unless Irssi::settings_get_bool('usercount_show_ircops');
+}
+
+sub refresh {
+   if ($timeout_tag > 0) {
+      Irssi::timeout_remove($timeout_tag);
+      $timeout_tag = 0;
+   }
+   Irssi::statusbar_items_redraw('usercount');
+}
+
+sub refresh_check {
+   my $channel = shift;
+   my $wi = ref Irssi::active_win() ? Irssi::active_win()->{active} : 0;
+
+   return unless ref $wi && ref $channel;
+   return if $wi->{name} ne $channel->{name};
+   return if $wi->{server}->{tag} ne $channel->{server}->{tag};
+
+   # don't refresh immediately, or we'll end up refreshing 
+   # a lot around netsplits
+   $recalc = 1;
+   Irssi::timeout_remove($timeout_tag) if ($timeout_tag > 0);
+   $timeout_tag = Irssi::timeout_add(500, 'refresh', undef);
+}
+
+sub refresh_recalc {
+  $recalc = 1;
+  refresh();
+}
+
+$recalc = 1;
+$timeout_tag = 0;
+
+Irssi::settings_add_bool('usercount', 'usercount_show_zero', 1);
+Irssi::settings_add_bool('usercount', 'usercount_show_ircops', 0);
+Irssi::settings_add_bool('usercount', 'usercount_show_halfops', 1);
+
+Irssi::statusbar_item_register('usercount', undef, 'usercount');
+Irssi::statusbars_recreate_items();
+
+Irssi::signal_add_last('nicklist new', 'refresh_check');
+Irssi::signal_add_last('nicklist remove', 'refresh_check');
+Irssi::signal_add_last('nick mode changed', 'refresh_check');
+Irssi::signal_add_last('setup changed', 'refresh_recalc');
+Irssi::signal_add_last('window changed', 'refresh_recalc');
+Irssi::signal_add_last('window item changed', 'refresh_recalc');
+
index a0dc6af1d8a363188f5af702176485a3a3776f4d..d27958c7090d7f807ec2049616ec716a27ec5938 100644 (file)
@@ -10,6 +10,9 @@ if HAVE_PERL
 PERLDIR=perl
 endif
 
+pkginc_srcdir=$(pkgincludedir)/src
+pkginc_src_HEADERS = \
+       common.h
 noinst_HEADERS = \
        common.h
 
index f48b9a92e3566db562f9ed353d2c2984fd5e397e..f3cc8e60962f05b0d0e9b27b910b81e2206466fa 100644 (file)
 #  include <gmodule.h>
 #endif
 
+#ifdef USE_GC
+#  define g_free(x) G_STMT_START { (x) = NULL; } G_STMT_END
+#endif
+
 #if defined (UOFF_T_INT)
 typedef unsigned int uoff_t;
 #elif defined (UOFF_T_LONG)
 typedef unsigned long uoff_t;
-#elif defined (UOFF_T_LONGLONG)
+#elif defined (UOFF_T_LONG_LONG)
 typedef unsigned long long uoff_t;
 #else
-typedef unsigned int uoff_t;
+#  error uoff_t size not set
 #endif
 
 /* input functions */
index 6a6aeef31eae3396bcdfa3a7fd1b56b24812282d..55ab73c4d8717a581816d40a6371167f5897de45 100644 (file)
@@ -38,6 +38,7 @@ libcore_a_SOURCES = \
        pidwait.c \
        queries.c \
        rawlog.c \
+       recode.c \
        servers.c \
        servers-reconnect.c \
        servers-setup.c \
@@ -85,6 +86,7 @@ noinst_HEADERS = \
        pidwait.h \
        queries.h \
        rawlog.h \
+       recode.h \
        servers.h \
        servers-reconnect.h \
        servers-setup.h \
index 9a92b896caac2bc2dd6692aae625c2b8ef704474..79d8f0e7c826fc81da9960fc993ec15beecfb39e 100644 (file)
@@ -89,6 +89,7 @@ void channel_destroy(CHANNEL_REC *channel)
        g_free_not_null(channel->key);
        g_free(channel->mode);
        g_free(channel->name);
+       g_free(channel->visible_name);
 
         channel->type = 0;
        g_free(channel);
@@ -219,7 +220,7 @@ static void event_connected(SERVER_REC *server)
 
 static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag)
 {
-       const char *flags = server->get_nick_flags();
+       const char *flags = server->get_nick_flags(server);
 
        return strchr(flags, flag) == NULL ||
                (flag == flags[0] && nick->op) ||
@@ -259,7 +260,7 @@ void channel_send_autocommands(CHANNEL_REC *channel)
                         continue;
 
                nick = nicklist_find_mask(channel,
-                                         channel->server->isnickflag(*botnick) ?
+                                         channel->server->isnickflag(channel->server, *botnick) ?
                                          botnick+1 : botnick);
                if (nick != NULL &&
                    match_nick_flags(channel->server, nick, *botnick)) {
index 722d3df58955473c0c943e87429497123d94ee5b..c7cc87d85313b14a8b9f3e83020a8f5468770e35 100644 (file)
@@ -24,6 +24,7 @@
 #include "commands.h"
 #include "special-vars.h"
 #include "settings.h"
+#include "recode.h"
 
 #include "chat-protocols.h"
 #include "servers.h"
@@ -40,7 +41,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
         CHAT_PROTOCOL_REC *proto;
        SERVER_CONNECT_REC *conn;
        GHashTable *optlist;
-       char *addr, *portstr, *password, *nick, *chatnet, *host;
+       char *addr, *portstr, *password, *nick, *chatnet, *host, *tmp;
        void *free_arg;
 
        g_return_val_if_fail(data != NULL, NULL);
@@ -67,6 +68,10 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
        /* connect to server */
        chatnet = proto == NULL ? NULL :
                g_hash_table_lookup(optlist, proto->chatnet);
+
+       if (chatnet == NULL)
+               chatnet = g_hash_table_lookup(optlist, "network");
+       
        conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
                                  atoi(portstr), chatnet, password, nick);
        if (proto == NULL)
@@ -88,7 +93,22 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
        else if (g_hash_table_lookup(optlist, "4") != NULL)
                conn->family = AF_INET;
 
-       if(g_hash_table_lookup(optlist, "ssl") != NULL)
+       if (g_hash_table_lookup(optlist, "ssl") != NULL)
+               conn->use_ssl = TRUE;
+       if ((tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL)
+               conn->ssl_cert = g_strdup(tmp);
+       if ((tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL)
+               conn->ssl_pkey = g_strdup(tmp);
+       if (g_hash_table_lookup(optlist, "ssl_verify") != NULL)
+               conn->ssl_verify = TRUE;
+       if ((tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL)
+               conn->ssl_cafile = g_strdup(tmp);
+       if ((tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL)
+               conn->ssl_capath = g_strdup(tmp);
+       if ((conn->ssl_capath != NULL && conn->ssl_capath[0] != '\0')
+       ||  (conn->ssl_cafile != NULL && conn->ssl_cafile[0] != '\0'))
+               conn->ssl_verify = TRUE;
+       if ((conn->ssl_cert != NULL && conn->ssl_cert[0] != '\0') || conn->ssl_verify)
                conn->use_ssl = TRUE;
 
        if (g_hash_table_lookup(optlist, "!") != NULL)
@@ -112,9 +132,12 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
         return conn;
 }
 
-/* SYNTAX: CONNECT [-4 | -6] [-ssl] [-noproxy] [-silcnet <silcnet>]
-                   [-host <hostname>] [-rawlog <file>]
-                   <address>|<chatnet> [<port> [<password> [<nick>]]] */
+/* SYNTAX: CONNECT [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>]
+                   [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+                  [-noproxy] [-network <network>] [-host <hostname>]
+                  [-rawlog <file>]
+                  <address>|<chatnet> [<port> [<password> [<nick>]]] */
+/* NOTE: -network replaces the old -ircnet flag. */
 static void cmd_connect(const char *data)
 {
        SERVER_CONNECT_REC *conn;
@@ -214,9 +237,12 @@ static void sig_default_command_server(const char *data, SERVER_REC *server,
         signal_emit("command server connect", 3, data, server, item);
 }
 
-/* SYNTAX: SERVER [-4 | -6] [-ssl] [-noproxy] [-silcnet <silcnet>]
-                  [-host <hostname>] [-rawlog <file>]
+/* SYNTAX: SERVER [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>]
+                  [-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
+                 [-noproxy] [-network <network>] [-host <hostname>]
+                 [-rawlog <file>]
                   [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */
+/* NOTE: -network replaces the old -ircnet flag. */
 static void cmd_server_connect(const char *data, SERVER_REC *server)
 {
        SERVER_CONNECT_REC *conn;
@@ -289,6 +315,7 @@ static void cmd_quit(const char *data)
        signal_emit("gui exit", 0);
 }
 
+/* SYNTAX: JOIN [-invite] [-<server tag>] <channels> [<keys>] */
 static void cmd_join(const char *data, SERVER_REC *server)
 {
        GHashTable *optlist;
@@ -319,13 +346,13 @@ static void cmd_join(const char *data, SERVER_REC *server)
        cmd_params_free(free_arg);
 }
 
-/* SYNTAX: MSG [-channel] <target> <message> */
+/* SYNTAX: MSG [-<server tag>] [-channel | -nick] <targets> <message> */
 static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 {
        GHashTable *optlist;
-       char *target, *origtarget, *msg;
+       char *target, *origtarget, *msg, *recoded;
        void *free_arg;
-       int free_ret, target_type;
+       int free_ret, target_type = SEND_TARGET_NICK;
 
        g_return_if_fail(data != NULL);
 
@@ -344,42 +371,58 @@ static void cmd_msg(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
        if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0) {
                target = parse_special(&target, server, item,
                                       NULL, &free_ret, NULL, 0);
-               if (target != NULL && *target == '\0')
+               if (target != NULL && *target == '\0') {
+                       if (free_ret)
+                               g_free(target);
                        target = NULL;
+                       free_ret = FALSE;
+               }
        }
 
-       if (strcmp(target, "*") == 0) {
-                /* send to active channel/query */
-               if (item == NULL)
-                       cmd_param_error(CMDERR_NOT_JOINED);
-
-               target_type = IS_CHANNEL(item) ?
-                       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
-               target = (char *) window_item_get_target(item);
-       } else if (g_hash_table_lookup(optlist, "channel") != NULL)
-                target_type = SEND_TARGET_CHANNEL;
-       else if (g_hash_table_lookup(optlist, "nick") != NULL)
-               target_type = SEND_TARGET_NICK;
-       else {
-               /* Need to rely on server_ischannel(). If the protocol
-                  doesn't really know if it's channel or nick based on the
-                  name, it should just assume it's nick, because when typing
-                  text to channels it's always sent with /MSG -channel. */
-               target_type = server_ischannel(server, target) ?
-                       SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+       if (target != NULL) {
+               if (strcmp(target, "*") == 0) {
+                        /* send to active channel/query */
+                       if (item == NULL)
+                               cmd_param_error(CMDERR_NOT_JOINED);
+
+                       target_type = IS_CHANNEL(item) ?
+                               SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+                       target = (char *) window_item_get_target(item);
+               } else if (g_hash_table_lookup(optlist, "channel") != NULL)
+                        target_type = SEND_TARGET_CHANNEL;
+               else if (g_hash_table_lookup(optlist, "nick") != NULL)
+                       target_type = SEND_TARGET_NICK;
+               else {
+                       /* Need to rely on server_ischannel(). If the protocol
+                          doesn't really know if it's channel or nick based on
+                          the name, it should just assume it's nick, because 
+                          when typing text to channels it's always sent with
+                          /MSG -channel. */
+                       target_type = server_ischannel(server, target) ?
+                               SEND_TARGET_CHANNEL : SEND_TARGET_NICK;
+               }
+       }
+       recoded = recode_out(server, msg, target);
+       if (target != NULL) {
+               signal_emit("server sendmsg", 4, server, target, recoded,
+                           GINT_TO_POINTER(target_type));
        }
-
-       if (target != NULL)
-               server->send_message(server, target, msg, target_type);
-
        signal_emit(target != NULL && target_type == SEND_TARGET_CHANNEL ?
                    "message own_public" : "message own_private", 4,
-                   server, msg, target, origtarget);
-
+                   server, recoded, target, origtarget);
+                   
+       g_free(recoded);
        if (free_ret && target != NULL) g_free(target);
        cmd_params_free(free_arg);
 }
 
+static void sig_server_sendmsg(SERVER_REC *server, const char *target,
+                              const char *msg, void *target_type_p)
+{
+       server->send_message(server, target, msg,
+                            GPOINTER_TO_INT(target_type_p));
+}
+
 static void cmd_foreach(const char *data, SERVER_REC *server,
                        WI_ITEM_REC *item)
 {
@@ -442,9 +485,10 @@ void chat_commands_init(void)
        command_bind("foreach channel", NULL, (SIGNAL_FUNC) cmd_foreach_channel);
        command_bind("foreach query", NULL, (SIGNAL_FUNC) cmd_foreach_query);
 
-        signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
+       signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
+       signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
 
-       command_set_options("connect", "4 6 !! ssl +host noproxy -rawlog");
+       command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey ssl_verify +ssl_cafile +ssl_capath +host noproxy -rawlog");
        command_set_options("join", "invite");
        command_set_options("msg", "channel nick");
 }
@@ -464,4 +508,5 @@ void chat_commands_deinit(void)
        command_unbind("foreach query", (SIGNAL_FUNC) cmd_foreach_query);
 
         signal_remove("default command server", (SIGNAL_FUNC) sig_default_command_server);
+       signal_remove("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
 }
index f7b0ec627ce2d06dbb77fbf014b6f445b6cce179..9e43f859a81ec01335017d8b3f6644b1c3ff8452 100644 (file)
@@ -118,7 +118,8 @@ static void sig_connected(SERVER_REC *server)
 
        g_return_if_fail(IS_SERVER(server));
 
-       if (server->connrec->chatnet == NULL || server->session_reconnect)
+       if (server->connrec->chatnet == NULL || server->session_reconnect ||
+           server->connrec->no_autojoin_channels)
                return;
 
        rec = chatnet_find(server->connrec->chatnet);
index bfe3373cf9aa6bf05e09c4d2fef975188baa39ba..d9dc67af4e9d79b752a80f565aa21446045f8895 100644 (file)
@@ -89,6 +89,7 @@ command_module_find_and_remove(COMMAND_REC *rec, SIGNAL_FUNC func)
                        if (cb->func == func) {
                                rec->callbacks =
                                        g_slist_remove(rec->callbacks, cb);
+                               g_free(cb);
                                return rec;
                        }
                }
@@ -574,17 +575,17 @@ static int get_cmd_options(char **data, int ignore_unknown,
                        }
 
                        (*data)++;
-                       if (**data == '-' && i_isspace((*data)[1])) {
+                       if (**data == '-' && (*data)[1] == ' ') {
                                /* -- option means end of options even
                                   if next word starts with - */
                                (*data)++;
-                               while (i_isspace(**data)) (*data)++;
+                               while (**data == ' ') (*data)++;
                                break;
                        }
 
                        if (**data == '\0')
                                option = "-";
-                       else if (!i_isspace(**data))
+                       else if (**data != ' ')
                                option = cmd_get_param(data);
                        else {
                                option = "-";
@@ -629,7 +630,7 @@ static int get_cmd_options(char **data, int ignore_unknown,
                            *optlist[pos] == '!')
                                option = NULL;
 
-                       while (i_isspace(**data)) (*data)++;
+                       while (**data == ' ') (*data)++;
                        continue;
                }
 
@@ -647,7 +648,7 @@ static int get_cmd_options(char **data, int ignore_unknown,
                }
                option = NULL;
 
-               while (i_isspace(**data)) (*data)++;
+               while (**data == ' ') (*data)++;
        }
 
        return 0;
@@ -944,12 +945,18 @@ static void event_command(const char *line, SERVER_REC *server, void *item)
        parse_command(line, expand_aliases, server, item);
 }
 
+static int eval_recursion_depth=0;
 /* SYNTAX: EVAL <command(s)> */
 static void cmd_eval(const char *data, SERVER_REC *server, void *item)
 {
        g_return_if_fail(data != NULL);
+       if (eval_recursion_depth > 100) 
+               cmd_return_error(CMDERR_EVAL_MAX_RECURSE);
 
+       eval_recursion_depth++;
        eval_special_string(data, "", server, item);
+       eval_recursion_depth--;
 }
 
 /* SYNTAX: CD <directory> */
index 0b599f54c2c09e429eb99a1f73bd1c36b22f3fdf..c68c5b24b5a2dd8c59a63f9bdb08b28800201264 100644 (file)
@@ -37,7 +37,11 @@ enum {
        CMDERR_CHAN_NOT_FOUND, /* channel not found */
        CMDERR_CHAN_NOT_SYNCED, /* channel not fully synchronized yet */
        CMDERR_ILLEGAL_PROTO, /* requires different chat protocol than the active server */
-       CMDERR_NOT_GOOD_IDEA /* not good idea to do, -yes overrides this */
+       CMDERR_NOT_GOOD_IDEA, /* not good idea to do, -yes overrides this */
+       CMDERR_INVALID_TIME, /* invalid time specification */
+       CMDERR_INVALID_CHARSET, /* invalid charset specification */
+       CMDERR_EVAL_MAX_RECURSE, /* eval hit recursion limit */
+       CMDERR_PROGRAM_NOT_FOUND /* program not found */
 };
 
 /* Return the full command for `alias' */
index 7125344ff0bfc08b79db1cdabe509fa39c6d1959..2f2f7329e00a92b02f7ef713388607beea54149d 100644 (file)
@@ -40,6 +40,7 @@
 #include "log.h"
 #include "rawlog.h"
 #include "ignore.h"
+#include "recode.h"
 
 #include "channels.h"
 #include "queries.h"
@@ -60,6 +61,7 @@ void log_away_deinit(void);
 int irssi_gui;
 int irssi_init_finished;
 int reload_config;
+time_t client_start_time;
 
 static char *irssi_dir, *irssi_config_file;
 static GSList *dialog_type_queue, *dialog_text_queue;
@@ -155,8 +157,8 @@ static void sig_init_finished(void)
 void core_init_paths(int argc, char *argv[])
 {
        static struct poptOption options[] = {
-               { "config", 0, POPT_ARG_STRING, NULL, 0, "Configuration file location (~/.silc/config)", "PATH" },
-               { "home", 0, POPT_ARG_STRING, NULL, 0, "Irssi home dir location (~/.silc)", "PATH" },
+               { "config", 0, POPT_ARG_STRING, NULL, 0, "Configuration file location (~/.irssi/config)", "PATH" },
+               { "home", 0, POPT_ARG_STRING, NULL, 0, "Irssi home dir location (~/.irssi)", "PATH" },
                { NULL, '\0', 0, NULL }
        };
        const char *home;
@@ -214,6 +216,7 @@ void core_init(int argc, char *argv[])
 {
        dialog_type_queue = NULL;
        dialog_text_queue = NULL;
+       client_start_time = time(NULL);
 
        modules_init();
 #ifndef WIN32
@@ -241,6 +244,7 @@ void core_init(int argc, char *argv[])
        log_init();
        log_away_init();
        rawlog_init();
+       recode_init();
 
        channels_init();
        queries_init();
@@ -249,7 +253,7 @@ void core_init(int argc, char *argv[])
        chat_commands_init();
 
        settings_add_str("misc", "ignore_signals", "");
-       settings_add_bool("misc", "override_coredump_limit", TRUE);
+       settings_add_bool("misc", "override_coredump_limit", FALSE);
 
 #ifdef HAVE_SYS_RESOURCE_H
        getrlimit(RLIMIT_CORE, &orig_core_rlimit);
@@ -276,6 +280,7 @@ void core_deinit(void)
        queries_deinit();
        channels_deinit();
 
+       recode_deinit();
        rawlog_deinit();
        log_away_deinit();
        log_deinit();
index 74317166a13eede62a1714bc0a80d79620400eb7..ceff244b430be1253048e15613d9d095ae29ae0e 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __IRSSI_CORE_H
 #define __IRSSI_CORE_H
 
+#include "common.h"
+
 /* for determining what GUI is currently in use: */
 #define IRSSI_GUI_NONE 0
 #define IRSSI_GUI_TEXT 1
@@ -12,6 +14,7 @@
 extern int irssi_gui;
 extern int irssi_init_finished; /* TRUE after "irssi init finished" signal is sent */
 extern int reload_config; /* TRUE after received SIGHUP. */
+extern time_t client_start_time;
 
 void core_init_paths(int argc, char *argv[]);
 
index 7ebc4f208db1022c55b55b83a95aa43b211a4edb..0d7a6141267f29a3933a725a6c989956ff49ff1f 100644 (file)
@@ -18,6 +18,7 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#include "core.h"
 #include "module.h"
 #include "modules.h"
 #include "signals.h"
@@ -52,7 +53,6 @@ static int timer_tag;
 
 static EXPANDO_REC *char_expandos[255];
 static GHashTable *expandos;
-static time_t client_start_time;
 static char *last_sent_msg, *last_sent_msg_body;
 static char *last_privmsg_from, *last_public_from;
 static char *sysname, *sysrelease, *sysarch;
@@ -133,7 +133,7 @@ void expando_add_signal(const char *key, const char *signal, ExpandoArg arg)
 /* Destroy expando */
 void expando_destroy(const char *key, EXPANDO_FUNC func)
 {
-       gpointer origkey;
+       gpointer origkey, value;
         EXPANDO_REC *rec;
 
        g_return_if_fail(key != NULL || *key == '\0');
@@ -146,8 +146,9 @@ void expando_destroy(const char *key, EXPANDO_FUNC func)
                        char_expandos[(int) (unsigned char) *key] = NULL;
                        g_free(rec);
                }
-       } else if (g_hash_table_lookup_extended(expandos, key, &origkey,
-                                               (gpointer *) &rec)) {
+       } else if (g_hash_table_lookup_extended(expandos, key,
+                                               &origkey, &value)) {
+               rec = value;
                if (rec->func == func) {
                        g_hash_table_remove(expandos, key);
                        g_free(origkey);
@@ -259,6 +260,13 @@ EXPANDO_FUNC expando_find_long(const char *key)
        return rec == NULL ? NULL : rec->func;
 }
 
+static gboolean free_expando(gpointer key, gpointer value, gpointer user_data)
+{
+       g_free(key);
+       g_free(value);
+       return TRUE;
+}
+
 /* last person who sent you a MSG */
 static char *expando_lastmsg(SERVER_REC *server, void *item, int *free_ret)
 {
@@ -333,8 +341,23 @@ static char *expando_cmdchar(SERVER_REC *server, void *item, int *free_ret)
 
 /* modes of current channel, if any */
 static char *expando_chanmode(SERVER_REC *server, void *item, int *free_ret)
-{
-       return !IS_CHANNEL(item) ? NULL : CHANNEL(item)->mode;
+{ 
+       char *cmode;
+
+       *free_ret = FALSE;
+
+       if (!IS_CHANNEL(item))
+               return NULL;
+
+        if (!settings_get_bool("chanmode_expando_strip"))
+               return CHANNEL(item)->mode;
+
+       *free_ret = TRUE;
+       cmode = g_strdup(CHANNEL(item)->mode);
+       if (strchr(cmode, ' ') != NULL)
+               *(strchr(cmode, ' ')) = 0;
+
+       return cmode;
 }
 
 /* current nickname */
@@ -559,8 +582,8 @@ void expandos_init(void)
 #endif
        settings_add_str("misc", "STATUS_OPER", "*");
        settings_add_str("lookandfeel", "timestamp_format", "%H:%M");
+       settings_add_bool("lookandfeel", "chanmode_expando_strip", FALSE);
 
-       client_start_time = time(NULL);
        last_sent_msg = NULL; last_sent_msg_body = NULL;
        last_privmsg_from = NULL; last_public_from = NULL;
         last_timestamp = 0;
@@ -682,13 +705,7 @@ void expandos_deinit(void)
        for (n = 0; n < sizeof(char_expandos)/sizeof(char_expandos[0]); n++)
                g_free_not_null(char_expandos[n]);
 
-       expando_destroy("sysname", expando_sysname);
-       expando_destroy("sysrelease", expando_sysrelease);
-       expando_destroy("sysarch", expando_sysarch);
-       expando_destroy("topic", expando_topic);
-       expando_destroy("tag", expando_servertag);
-       expando_destroy("chatnet", expando_chatnet);
-
+       g_hash_table_foreach_remove(expandos, free_expando, NULL);
         g_hash_table_destroy(expandos);
 
        g_free_not_null(last_sent_msg); g_free_not_null(last_sent_msg_body);
index b7daf27568abb389bc2a65ec4a6fa37ae170f223..a1e96544adba037da20f50f0b8716bd5906a972e 100644 (file)
@@ -90,6 +90,7 @@ static int remove_newline(LINEBUF_REC *rec)
 int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer)
 {
        LINEBUF_REC *rec;
+       int ret;
 
        g_return_val_if_fail(data != NULL, -1);
        g_return_val_if_fail(output != NULL, -1);
@@ -122,8 +123,9 @@ int line_split(const char *data, int len, char **output, LINEBUF_REC **buffer)
                }
        }
 
+       ret = remove_newline(rec);
        *output = rec->str;
-       return remove_newline(rec);
+       return ret;
 }
 
 void line_split_free(LINEBUF_REC *buffer)
index 724e4b4a1c8b4eee6639eea2a9996baeca029eb0..233f3f59f1d407d1182d0b1bf288fee0291bb0f8 100644 (file)
@@ -38,16 +38,13 @@ static void sig_log_written(LOG_REC *log)
 
 static void awaylog_open(void)
 {
-       const char *fname, *levelstr;
+       const char *fname;
        LOG_REC *log;
        int level;
 
        fname = settings_get_str("awaylog_file");
-       levelstr = settings_get_str("awaylog_level");
-       if (*fname == '\0' || *levelstr == '\0') return;
-
-       level = level2bits(levelstr);
-       if (level == 0) return;
+       level = settings_get_level("awaylog_level");
+       if (*fname == '\0' || level == 0) return;
 
        log = log_find(fname);
        if (log != NULL && log->handle != -1)
@@ -101,12 +98,16 @@ static void sig_away_changed(SERVER_REC *server)
 
 void log_away_init(void)
 {
+       char *awaylog_file;
+
        awaylog = NULL;
        away_filepos = 0;
        away_msgs = 0;
 
-       settings_add_str("log", "awaylog_file", IRSSI_DIR_SHORT"/away.log");
-       settings_add_str("log", "awaylog_level", "msgs hilight");
+       awaylog_file = g_strconcat(get_irssi_dir(), "/away.log", NULL);
+       settings_add_str("log", "awaylog_file", awaylog_file);
+       g_free(awaylog_file);
+       settings_add_level("log", "awaylog_level", "msgs hilight");
 
        signal_add("log written", (SIGNAL_FUNC) sig_log_written);
        signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
index 75768601839e558bb29d471e52ba10c53363a0fb..4d49402e371798e8ae452729ac54dfef5667bc84 100644 (file)
@@ -246,18 +246,28 @@ void log_write_rec(LOG_REC *log, const char *str, int level)
         g_free_not_null(colorstr);
 }
 
+static int itemcmp(const char *patt, const char *item)
+{
+       /* returns 0 on match, nonzero otherwise */
+
+       if (item == NULL)
+               return g_strcasecmp(patt, "*") != 0;
+       if (*patt == '*')
+               return 0;
+        return g_strcasecmp(patt, item);
+}
+
 LOG_ITEM_REC *log_item_find(LOG_REC *log, int type, const char *item,
                            const char *servertag)
 {
        GSList *tmp;
 
        g_return_val_if_fail(log != NULL, NULL);
-       g_return_val_if_fail(item != NULL, NULL);
 
        for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
                LOG_ITEM_REC *rec = tmp->data;
 
-               if (rec->type == type && g_strcasecmp(rec->name, item) == 0 &&
+               if (rec->type == type && itemcmp(rec->name, item) == 0 &&
                    (rec->servertag == NULL || (servertag != NULL &&
                        g_strcasecmp(rec->servertag, servertag) == 0)))
                        return rec;
index 00f9d096b394fbf12cef0c9e0277fad1f53857af..3dbc7bad0621797bde94f7d3405d4b67a4059e5a 100644 (file)
@@ -173,37 +173,6 @@ int strarray_find(char **array, const char *item)
        return -1;
 }
 
-int execute(const char *cmd)
-{
-       char **args;
-#ifndef WIN32
-       int pid;
-#endif
-
-       g_return_val_if_fail(cmd != NULL, -1);
-
-#ifndef WIN32
-       pid = fork();
-       if (pid == -1) return FALSE;
-       if (pid != 0) {
-               pidwait_add(pid);
-               return pid;
-       }
-
-       args = g_strsplit(cmd, " ", -1);
-       execvp(args[0], args);
-       g_strfreev(args);
-
-       _exit(99);
-       return -1;
-#else
-       args = g_strsplit(cmd, " ", -1);
-       _spawnvp(_P_DETACH, args[0], args);
-       g_strfreev(args);
-       return 0;
-#endif
-}
-
 GSList *gslist_find_string(GSList *list, const char *key)
 {
        for (list = list; list != NULL; list = list->next)
@@ -808,3 +777,149 @@ char *escape_string(const char *str)
 
        return ret;
 }
+
+int strocpy(char *dest, const char *src, size_t dstsize)
+{
+       if (dstsize == 0)
+               return -1;
+
+       while (*src != '\0' && dstsize > 1) {
+               *dest++ = *src++;
+               dstsize--;
+       }
+
+       *dest++ = '\0';
+       return *src == '\0' ? 0 : -1;
+}
+
+int nearest_power(int num)
+{
+       int n = 1;
+
+       while (n < num) n <<= 1;
+       return n;
+}
+
+int parse_time_interval(const char *time, int *msecs)
+{
+       const char *desc;
+       int number, sign, len, ret;
+
+       *msecs = 0;
+
+       /* max. return value is around 24 days */
+       number = 0; sign = 1; ret = TRUE;
+       for (;;) {
+               if (*time == '-') {
+                       sign = -sign;
+                       time++;
+                       continue;
+               }
+
+               if (i_isdigit(*time)) {
+                       number = number*10 + (*time - '0');
+                       time++;
+                       continue;
+               }
+
+               /* skip punctuation */
+               while (*time != '\0' && i_ispunct(*time))
+                       time++;
+
+               /* get description */
+               for (len = 0, desc = time; i_isalpha(*time); time++)
+                       len++;
+
+               if (len == 0) {
+                       *msecs += number * 1000; /* assume seconds */
+                       *msecs *= sign;
+                       return TRUE;
+               }
+
+               if (g_strncasecmp(desc, "days", len) == 0) {
+                       if (number > 24) {
+                               /* would overflow */
+                               return FALSE;
+                       }
+                       *msecs += number * 1000*3600*24;
+               } else if (g_strncasecmp(desc, "hours", len) == 0)
+                       *msecs += number * 1000*3600;
+               else if (g_strncasecmp(desc, "minutes", len) == 0 ||
+                        g_strncasecmp(desc, "mins", len) == 0)
+                       *msecs += number * 1000*60;
+               else if (g_strncasecmp(desc, "seconds", len) == 0 ||
+                        g_strncasecmp(desc, "secs", len) == 0)
+                       *msecs += number * 1000;
+               else if (g_strncasecmp(desc, "milliseconds", len) == 0 ||
+                        g_strncasecmp(desc, "millisecs", len) == 0 ||
+                        g_strncasecmp(desc, "mseconds", len) == 0 ||
+                        g_strncasecmp(desc, "msecs", len) == 0)
+                       *msecs += number;
+               else {
+                       ret = FALSE;
+               }
+
+               /* skip punctuation */
+               while (*time != '\0' && i_ispunct(*time))
+                       time++;
+
+               if (*time == '\0')
+                       break;
+
+               number = 0;
+       }
+
+       *msecs *= sign;
+       return ret;
+}
+
+int parse_size(const char *size, int *bytes)
+{
+       const char *desc;
+       int number, len;
+
+       *bytes = 0;
+
+       /* max. return value is about 1.6 years */
+       number = 0;
+       while (*size != '\0') {
+               if (i_isdigit(*size)) {
+                       number = number*10 + (*size - '0');
+                       size++;
+                       continue;
+               }
+
+               /* skip punctuation */
+               while (*size != '\0' && i_ispunct(*size))
+                       size++;
+
+               /* get description */
+               for (len = 0, desc = size; i_isalpha(*size); size++)
+                       len++;
+
+               if (len == 0) {
+                       if (number == 0) {
+                               /* "0" - allow it */
+                               return TRUE;
+                       }
+
+                       *bytes += number*1024; /* assume kilobytes */
+                       return FALSE;
+               }
+
+               if (g_strncasecmp(desc, "gbytes", len) == 0)
+                       *bytes += number * 1024*1024*1024;
+               if (g_strncasecmp(desc, "mbytes", len) == 0)
+                       *bytes += number * 1024*1024;
+               if (g_strncasecmp(desc, "kbytes", len) == 0)
+                       *bytes += number * 1024;
+               if (g_strncasecmp(desc, "bytes", len) == 0)
+                       *bytes += number;
+
+               /* skip punctuation */
+               while (*size != '\0' && i_ispunct(*size))
+                       size++;
+       }
+
+       return TRUE;
+}
index 959460778a62a1f011eedddb247967ae7cd28ede..35a9dfc8a5f316e53e669cee43e8daf3612e0838 100644 (file)
@@ -8,14 +8,6 @@
 typedef void* (*FOREACH_FIND_FUNC) (void *item, void *data);
 typedef int (*COLUMN_LEN_FUNC)(void *data);
 
-static inline int nearest_power(int num)
-{
-       int n = 1;
-
-       while (n < num) n <<= 1;
-       return n;
-}
-
 /* Returns 1 if tv1 > tv2, -1 if tv2 > tv1 or 0 if they're equal. */
 int g_timeval_cmp(const GTimeVal *tv1, const GTimeVal *tv2);
 /* Returns "tv1 - tv2", returns the result in milliseconds. Note that
@@ -29,8 +21,6 @@ int strarray_length(char **array);
 /* return index of `item' in `array' or -1 if not found */
 int strarray_find(char **array, const char *item);
 
-int execute(const char *cmd); /* returns pid or -1 = error */
-
 GSList *gslist_find_string(GSList *list, const char *key);
 GSList *gslist_find_icase_string(GSList *list, const char *key);
 GList *glist_find_string(GList *list, const char *key);
@@ -111,4 +101,13 @@ int expand_escape(const char **data);
 /* Escape all '"', "'" and '\' chars with '\' */
 char *escape_string(const char *str);
 
+/* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */
+int strocpy(char *dest, const char *src, size_t dstsize);
+
+int nearest_power(int num);
+
+/* Returns TRUE / FALSE */
+int parse_time_interval(const char *time, int *msecs);
+int parse_size(const char *size, int *bytes);
+
 #endif
index 7b90d802940a106cb9f70c4b6577531c16f32f61..2ae029e845077769b2d16093af72ade6439ac33f 100644 (file)
@@ -163,6 +163,7 @@ static int module_load_name(const char *path, const char *rootmodule,
        GModule *gmodule;
         MODULE_REC *module;
        MODULE_FILE_REC *rec;
+       gpointer value1, value2;
        char *initfunc, *deinitfunc;
         int found;
 
@@ -178,11 +179,14 @@ static int module_load_name(const char *path, const char *rootmodule,
        /* get the module's init() and deinit() functions */
        initfunc = module_get_func(rootmodule, submodule, "init");
        deinitfunc = module_get_func(rootmodule, submodule, "deinit");
-       found = g_module_symbol(gmodule, initfunc, (gpointer *) &module_init) &&
-               g_module_symbol(gmodule, deinitfunc, (gpointer *) &module_deinit);
+       found = g_module_symbol(gmodule, initfunc, &value1) &&
+               g_module_symbol(gmodule, deinitfunc, &value2);
        g_free(initfunc);
        g_free(deinitfunc);
 
+       module_init = value1;
+       module_deinit = value2;
+
        if (!found) {
                module_error(MODULE_ERROR_INVALID, NULL,
                             rootmodule, submodule);
index dc37318912d3bcdd0f5135801ccb2738c850e07a..a04e31ccf37134af12a70441df533f0c1998a70b 100644 (file)
@@ -177,10 +177,11 @@ static void uniq_destroy_str(gpointer key, gpointer value)
 void module_uniq_destroy(const char *module)
 {
        GHashTable *idlist;
-       gpointer key;
+       gpointer key, value;
+
+       if (g_hash_table_lookup_extended(idlookup, module, &key, &value)) {
+               idlist = value;
 
-       if (g_hash_table_lookup_extended(idlookup, module, &key,
-                                        (gpointer *) &idlist)) {
                g_hash_table_remove(idlookup, key);
                g_free(key);
 
@@ -188,8 +189,9 @@ void module_uniq_destroy(const char *module)
                g_hash_table_destroy(idlist);
        }
 
-       if (g_hash_table_lookup_extended(stridlookup, module, &key,
-                                        (gpointer *) &idlist)) {
+       if (g_hash_table_lookup_extended(stridlookup, module, &key, &value)) {
+               idlist = value;
+
                g_hash_table_remove(stridlookup, key);
                g_free(key);
 
index ce6be49af5dc679eebc8d16fc1f61fd1eac3d7bd..14e08664776a64d69d627f5a4b3aa9c0a52917cd 100644 (file)
@@ -40,7 +40,7 @@ typedef struct {
 
 static int g_io_channel_write_block(GIOChannel *channel, void *data, int len)
 {
-        unsigned int ret;
+        gsize ret;
        int err, sent;
 
        sent = 0;
@@ -56,7 +56,7 @@ static int g_io_channel_write_block(GIOChannel *channel, void *data, int len)
 static int g_io_channel_read_block(GIOChannel *channel, void *data, int len)
 {
        time_t maxwait;
-        unsigned int ret;
+        gsize ret;
        int err, received;
 
        maxwait = time(NULL)+2;
@@ -101,6 +101,8 @@ int net_gethostbyname_nonblock(const char *addr, GIOChannel *pipe,
 #endif
 
        /* child */
+       srand(time(NULL));
+
         memset(&rec, 0, sizeof(rec));
        rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6);
        if (rec.error == 0) {
index fd1039d67b7fb69c044d0f942c1ed71f3d14e71b..f8aac80ce2e95212a0aa1a63387e543f5d574e07 100644 (file)
 #include "network.h"
 #include "net-sendbuffer.h"
 
-struct _NET_SENDBUF_REC {
-       GIOChannel *handle;
-
-        int send_tag;
-       int bufsize;
-       int bufpos;
-       char *buffer; /* Buffer is NULL until it's actually needed. */
-};
-
 static GSList *buffers;
 
 /* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE
index d2388b419746ff9361cca613dac0f0f301fd643d..f492821e66ac0c394b0f8e79ff1be700dc6fc7fb 100644 (file)
@@ -3,6 +3,15 @@
 
 #define DEFAULT_BUFFER_SIZE 8192
 
+struct _NET_SENDBUF_REC {
+        GIOChannel *handle;
+
+        int send_tag;
+        int bufsize;
+        int bufpos;
+        char *buffer; /* Buffer is NULL until it's actually needed. */
+};
+
 /* Create new buffer - if `bufsize' is zero or less, DEFAULT_BUFFER_SIZE
    is used */
 NET_SENDBUF_REC *net_sendbuffer_create(GIOChannel *handle, int bufsize);
index c88b5050de51a36d14dfbdb25017ce31a4c25744..68fb8ea82776b837f4b00c618a06ea5963104b1e 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "module.h"
 #include "network.h"
+#include "misc.h"
 
 #ifdef HAVE_OPENSSL
 
@@ -36,25 +37,67 @@ typedef struct
        gint fd;
        GIOChannel *giochan;
        SSL *ssl;
-       X509 *cert;
+       SSL_CTX *ctx;
+       unsigned int got_cert:1;
+       unsigned int verify:1;
 } GIOSSLChannel;
        
+static SSL_CTX *ssl_ctx = NULL;
+
 static void irssi_ssl_free(GIOChannel *handle)
 {
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
        g_io_channel_unref(chan->giochan);
        SSL_free(chan->ssl);
+       if (chan->ctx != ssl_ctx)
+               SSL_CTX_free(chan->ctx);
        g_free(chan);
 }
 
+static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, X509 *cert)
+{
+       if (SSL_get_verify_result(ssl) != X509_V_OK) {
+               unsigned char md[EVP_MAX_MD_SIZE];
+               unsigned int n;
+               char *str;
+
+               g_warning("Could not verify SSL servers certificate:");
+               if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
+                       g_warning("  Could not get subject-name from peer certificate");
+               else {
+                       g_warning("  Subject : %s", str);
+                       free(str);
+               }
+               if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
+                       g_warning("  Could not get issuer-name from peer certificate");
+               else {
+                       g_warning("  Issuer  : %s", str);
+                       free(str);
+               }
+               if (! X509_digest(cert, EVP_md5(), md, &n))
+                       g_warning("  Could not get fingerprint from peer certificate");
+               else {
+                       char hex[] = "0123456789ABCDEF";
+                       char fp[EVP_MAX_MD_SIZE*3];
+                       if (n < sizeof(fp)) {
+                               unsigned int i;
+                               for (i = 0; i < n; i++) {
+                                       fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
+                                       fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
+                                       fp[i*3+2] = i == n - 1 ? '\0' : ':';
+                               }
+                               g_warning("  MD5 Fingerprint : %s", fp);
+                       }
+               }
+               return FALSE;
+       }
+       return TRUE;
+}
+
+
 #if GLIB_MAJOR_VERSION < 2
 
-#ifdef G_CAN_INLINE
-G_INLINE_FUNC
-#else
-static
-#endif
-GIOError ssl_errno(gint e)
+static GIOError ssl_errno(gint e)
 {
        switch(e)
        {
@@ -72,15 +115,21 @@ GIOError ssl_errno(gint e)
 
 static GIOError irssi_ssl_cert_step(GIOSSLChannel *chan)
 {
+       X509 *cert;
        gint err;
        switch(err = SSL_do_handshake(chan->ssl))
        {
                case 1:
-                       if(!(chan->cert = SSL_get_peer_certificate(chan->ssl)))
+                       if(!(cert = SSL_get_peer_certificate(chan->ssl)))
                        {
                                g_warning("SSL server supplied no certificate");
                                return G_IO_ERROR_INVAL;
                        }
+                       if (chan->verify && ! irssi_ssl_verify(chan->ssl, chan->ctx, cert)) {
+                               X509_free(cert);
+                               return G_IO_ERROR_INVAL;
+                       }
+                       X509_free(cert);
                        return G_IO_ERROR_NONE;
                default:
                        if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
@@ -96,7 +145,7 @@ static GIOError irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
        gint err;
        
-       if(chan->cert == NULL)
+       if(! chan->got_cert)
        {
                gint cert_err = irssi_ssl_cert_step(chan);
                if(cert_err != G_IO_ERROR_NONE)
@@ -125,7 +174,7 @@ static GIOError irssi_ssl_write(GIOChannel *handle, gchar *buf, guint len, guint
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
        gint err;
 
-       if(chan->cert == NULL)
+       if(chan->got_cert)
        {
                gint cert_err = irssi_ssl_cert_step(chan);
                if(cert_err != G_IO_ERROR_NONE)
@@ -185,12 +234,7 @@ static GIOFuncs irssi_ssl_channel_funcs =
 
 #else /* GLIB_MAJOR_VERSION < 2 */
 
-#ifdef G_CAN_INLINE
-G_INLINE_FUNC
-#else
-static
-#endif
-GIOStatus ssl_errno(gint e)
+static GIOStatus ssl_errno(gint e)
 {
        switch(e)
        {
@@ -208,15 +252,21 @@ GIOStatus ssl_errno(gint e)
 
 static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan)
 {
+       X509 *cert;
        gint err;
        switch(err = SSL_do_handshake(chan->ssl))
        {
                case 1:
-                       if(!(chan->cert = SSL_get_peer_certificate(chan->ssl)))
+                       if(!(cert = SSL_get_peer_certificate(chan->ssl)))
                        {
                                g_warning("SSL server supplied no certificate");
                                return G_IO_STATUS_ERROR;
                        }
+                       if (chan->verify && ! irssi_ssl_verify(chan->ssl, chan->ctx, cert)) {
+                               X509_free(cert);
+                               return G_IO_STATUS_ERROR;
+                       }
+                       X509_free(cert);
                        return G_IO_STATUS_NORMAL;
                default:
                        if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
@@ -227,12 +277,12 @@ static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan)
        return G_IO_STATUS_ERROR;
 }
 
-static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret, GError **gerr)
+static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
 {
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
        gint err;
        
-       if(chan->cert == NULL)
+       if(! chan->got_cert)
        {
                gint cert_err = irssi_ssl_cert_step(chan);
                if(cert_err != G_IO_STATUS_NORMAL)
@@ -261,7 +311,7 @@ static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len
        GIOSSLChannel *chan = (GIOSSLChannel *)handle;
        gint err;
 
-       if(chan->cert == NULL)
+       if(! chan->got_cert)
        {
                gint cert_err = irssi_ssl_cert_step(chan);
                if(cert_err != G_IO_STATUS_NORMAL)
@@ -335,8 +385,6 @@ static GIOFuncs irssi_ssl_channel_funcs = {
 
 #endif
 
-static SSL_CTX *ssl_ctx = NULL;
-
 static gboolean irssi_ssl_init(void)
 {
        SSL_library_init();
@@ -353,13 +401,14 @@ static gboolean irssi_ssl_init(void)
 
 }
 
-static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
+static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycert, const char *mypkey, const char *cafile, const char *capath, gboolean verify)
 {
        GIOSSLChannel *chan;
        GIOChannel *gchan;
        int err, fd;
        SSL *ssl;
        X509 *cert = NULL;
+       SSL_CTX *ctx = NULL;
 
        g_return_val_if_fail(handle != NULL, NULL);
        
@@ -369,7 +418,52 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
        if(!(fd = g_io_channel_unix_get_fd(handle)))
                return NULL;
 
-       if(!(ssl = SSL_new(ssl_ctx)))
+       if (mycert && *mycert) {        
+               char *scert = NULL, *spkey = NULL;
+               if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
+                       g_error("Could not allocate memory for SSL context");
+                       return NULL;
+               }
+               scert = convert_home(mycert);
+               if (mypkey && *mypkey)
+                       spkey = convert_home(mypkey);
+               if (! SSL_CTX_use_certificate_file(ctx, scert, SSL_FILETYPE_PEM))
+                       g_warning("Loading of client certificate '%s' failed", mycert);
+               else if (! SSL_CTX_use_PrivateKey_file(ctx, spkey ? spkey : scert, SSL_FILETYPE_PEM))
+                       g_warning("Loading of private key '%s' failed", mypkey ? mypkey : mycert);
+               else if (! SSL_CTX_check_private_key(ctx))
+                       g_warning("Private key does not match the certificate");
+               g_free(scert);
+               g_free(spkey);
+       }
+
+       if ((cafile && *cafile) || (capath && *capath)) {
+               char *scafile = NULL;
+               char *scapath = NULL;
+               if (! ctx && (ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
+                       g_error("Could not allocate memory for SSL context");
+                       return NULL;
+               }
+               if (cafile && *cafile)
+                       scafile = convert_home(cafile);
+               if (capath && *capath)
+                       scapath = convert_home(capath);
+               if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) {
+                       g_warning("Could not load CA list for verifying SSL server certificate");
+                       g_free(scafile);
+                       g_free(scapath);
+                       SSL_CTX_free(ctx);
+                       return NULL;
+               }
+               g_free(scafile);
+               g_free(scapath);
+               verify = TRUE;
+       }
+
+       if (ctx == NULL)
+               ctx = ssl_ctx;
+       
+       if(!(ssl = SSL_new(ctx)))
        {
                g_warning("Failed to allocate SSL structure");
                return NULL;
@@ -378,6 +472,9 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
        if(!(err = SSL_set_fd(ssl, fd)))
        {
                g_warning("Failed to associate socket to SSL stream");
+               SSL_free(ssl);
+               if (ctx != ssl_ctx)
+                       SSL_CTX_free(ctx);
                return NULL;
        }
 
@@ -391,23 +488,38 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
                        case SSL_ERROR_WANT_WRITE:
                                        break;
                        default:
+                                       SSL_free(ssl);
+                                       if (ctx != ssl_ctx)
+                                               SSL_CTX_free(ctx);
                                        return NULL;
                }
        }
        else if(!(cert = SSL_get_peer_certificate(ssl)))
        {
                g_warning("SSL server supplied no certificate");
+               if (ctx != ssl_ctx)
+                       SSL_CTX_free(ctx);
+               SSL_free(ssl);
                return NULL;
        }
        else
+       {
+               if (verify && ! irssi_ssl_verify(ssl, ctx, cert)) {
+                       SSL_free(ssl);
+                       if (ctx != ssl_ctx)
+                               SSL_CTX_free(ctx);
+                       return NULL;
+               }
                X509_free(cert);
+       }
 
        chan = g_new0(GIOSSLChannel, 1);
        chan->fd = fd;
        chan->giochan = handle;
        chan->ssl = ssl;
-       chan->cert = cert;
-       g_io_channel_ref(handle);
+       chan->ctx = ctx;
+       chan->got_cert = cert != NULL;
+       chan->verify = verify;
 
        gchan = (GIOChannel *)chan;
        gchan->funcs = &irssi_ssl_channel_funcs;
@@ -416,16 +528,20 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle)
        return gchan;
 }
 
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip)
+GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
 {
-       GIOChannel *gret = net_connect_ip(ip, port, my_ip);
-       gret = irssi_ssl_get_iochannel(gret);
-       return gret;
+       GIOChannel *handle, *ssl_handle;
+
+       handle = net_connect_ip(ip, port, my_ip);
+       ssl_handle  = irssi_ssl_get_iochannel(handle, cert, pkey, cafile, capath, verify);
+       if (ssl_handle == NULL)
+               g_io_channel_unref(handle);
+       return ssl_handle;
 }
 
 #else /* HAVE_OPENSSL */
 
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip)
+GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
 {
        g_warning("Connection failed: SSL support not enabled in this build.");
        errno = ENOSYS;
index 5892ac6998bb6713562945ef3259dfa099a2060f..21235b2af8380470aac142452fa4e56993d81411 100644 (file)
@@ -27,7 +27,7 @@
 #  define INADDR_NONE INADDR_BROADCAST
 #endif
 
-union irssi_sockaddr_union {
+union sockaddr_union {
        struct sockaddr sa;
        struct sockaddr_in sin;
 #ifdef HAVE_IPV6
@@ -51,6 +51,11 @@ union irssi_sockaddr_union {
 /* Cygwin need this, don't know others.. */
 /*#define BLOCKING_SOCKETS 1*/
 
+IPADDR ip4_any = {
+       AF_INET,
+       { INADDR_ANY }
+};
+
 int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
 {
        if (ip1->family != ip2->family)
@@ -65,13 +70,7 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
 }
 
 
-/* copy IP to sockaddr */
-#ifdef G_CAN_INLINE
-G_INLINE_FUNC
-#else
-static
-#endif
-void sin_set_ip(union irssi_sockaddr_union *so, const IPADDR *ip)
+static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
 {
        if (ip == NULL) {
 #ifdef HAVE_IPV6
@@ -93,7 +92,7 @@ void sin_set_ip(union irssi_sockaddr_union *so, const IPADDR *ip)
                memcpy(&so->sin.sin_addr, &ip->ip, 4);
 }
 
-void sin_get_ip(const union irssi_sockaddr_union *so, IPADDR *ip)
+void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
 {
        ip->family = so->sin.sin_family;
 
@@ -105,27 +104,17 @@ void sin_get_ip(const union irssi_sockaddr_union *so, IPADDR *ip)
                memcpy(&ip->ip, &so->sin.sin_addr, 4);
 }
 
-#ifdef G_CAN_INLINE
-G_INLINE_FUNC
-#else
-static
-#endif
-void sin_set_port(union irssi_sockaddr_union *so, int port)
+static void sin_set_port(union sockaddr_union *so, int port)
 {
 #ifdef HAVE_IPV6
        if (so->sin.sin_family == AF_INET6)
-                so->sin6.sin6_port = htons(port);
+                so->sin6.sin6_port = htons((unsigned short)port);
        else
 #endif
                so->sin.sin_port = htons((unsigned short)port);
 }
 
-#ifdef G_CAN_INLINE
-G_INLINE_FUNC
-#else
-static
-#endif
-int sin_get_port(union irssi_sockaddr_union *so)
+static int sin_get_port(union sockaddr_union *so)
 {
 #ifdef HAVE_IPV6
        if (so->sin.sin_family == AF_INET6)
@@ -173,7 +162,7 @@ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip)
 /* Connect to socket with ip address */
 GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
 {
-       union irssi_sockaddr_union so;
+       union sockaddr_union so;
        int handle, ret, opt = 1;
 
        if (my_ip != NULL && ip->family != my_ip->family) {
@@ -201,10 +190,12 @@ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip)
        /* set our own address */
        if (my_ip != NULL) {
                sin_set_ip(&so, my_ip);
-               if (bind(handle, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
-                       /* failed, set it back to INADDR_ANY */
-                       sin_set_ip(&so, NULL);
-                       bind(handle, &so.sa, SIZEOF_SOCKADDR(so));
+               if (bind(handle, &so.sa, SIZEOF_SOCKADDR(so)) < 0) {
+                       int old_errno = errno;
+
+                       close(handle);
+                       errno = old_errno;
+                       return NULL;
                }
        }
 
@@ -274,15 +265,15 @@ void net_disconnect(GIOChannel *handle)
    address. */
 GIOChannel *net_listen(IPADDR *my_ip, int *port)
 {
-       union irssi_sockaddr_union so;
+       union sockaddr_union so;
        int ret, handle, opt = 1;
        socklen_t len;
 
        g_return_val_if_fail(port != NULL, NULL);
 
        memset(&so, 0, sizeof(so));
-       sin_set_port(&so, *port);
        sin_set_ip(&so, my_ip);
+       sin_set_port(&so, *port);
 
        /* create the socket */
        handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
@@ -331,7 +322,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
 /* Accept a connection on a socket */
 GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port)
 {
-       union irssi_sockaddr_union so;
+       union sockaddr_union so;
        int ret;
        socklen_t addrlen;
 
@@ -355,7 +346,7 @@ GIOChannel *net_accept(GIOChannel *handle, IPADDR *addr, int *port)
 /* Read data from socket, return number of bytes read, -1 = error */
 int net_receive(GIOChannel *handle, char *buf, int len)
 {
-        unsigned int ret;
+        gsize ret;
        int err;
 
        g_return_val_if_fail(handle != NULL, -1);
@@ -374,7 +365,7 @@ int net_receive(GIOChannel *handle, char *buf, int len)
 /* Transmit data, return number of bytes sent, -1 = error */
 int net_transmit(GIOChannel *handle, const char *data, int len)
 {
-        unsigned int ret;
+        gsize ret;
        int err;
 
        g_return_val_if_fail(handle != NULL, -1);
@@ -391,7 +382,7 @@ int net_transmit(GIOChannel *handle, const char *data, int len)
 /* Get socket address/port */
 int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
 {
-       union irssi_sockaddr_union so;
+       union sockaddr_union so;
        socklen_t addrlen;
 
        g_return_val_if_fail(handle != NULL, -1);
@@ -414,11 +405,12 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
 int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
 {
 #ifdef HAVE_IPV6
-       union irssi_sockaddr_union *so;
+       union sockaddr_union *so;
        struct addrinfo hints, *ai, *ailist;
-       int ret, count;
+       int ret, count_v4, count_v6, use_v4, use_v6;
 #else
        struct hostent *hp;
+       int count;
 #endif
 
        g_return_val_if_fail(addr != NULL, -1);
@@ -435,27 +427,54 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
        if (ret != 0)
                return ret;
 
-        count = 0;
-       for (ai = ailist; ai != NULL && count < 2; ai = ai->ai_next) {
-               so = (union irssi_sockaddr_union *) ai->ai_addr;
+       /* count IPs */
+        count_v4 = count_v6 = 0;
+       for (ai = ailist; ai != NULL; ai = ai->ai_next) {
+               if (ai->ai_family == AF_INET)
+                       count_v4++;
+               else if (ai->ai_family == AF_INET6)
+                       count_v6++;
+       }
 
-               if (ai->ai_family == AF_INET6 && ip6->family == 0) {
-                       sin_get_ip(so, ip6);
-                        count++;
-               } else if (ai->ai_family == AF_INET && ip4->family == 0) {
-                       sin_get_ip(so, ip4);
-                        count++;
+       if (count_v4 == 0 && count_v6 == 0)
+               return HOST_NOT_FOUND; /* shouldn't happen? */
+
+       /* if there are multiple addresses, return random one */
+       use_v4 = count_v4 <= 1 ? 0 : rand() % count_v4;
+       use_v6 = count_v6 <= 1 ? 0 : rand() % count_v6;
+
+       count_v4 = count_v6 = 0;
+       for (ai = ailist; ai != NULL; ai = ai->ai_next) {
+               so = (union sockaddr_union *) ai->ai_addr;
+
+               if (ai->ai_family == AF_INET) {
+                       if (use_v4 == count_v4)
+                               sin_get_ip(so, ip4);
+                        count_v4++;
+               } else if (ai->ai_family == AF_INET6) {
+                       if (use_v6 == count_v6)
+                               sin_get_ip(so, ip6);
+                       count_v6++;
                }
        }
        freeaddrinfo(ailist);
-       return count > 0 ? 0 : 1;
+       return 0;
 #else
        hp = gethostbyname(addr);
        if (hp == NULL)
                return h_errno;
 
+       /* count IPs */
+       count = 0;
+       while (hp->h_addr_list[count] != NULL)
+               count++;
+
+       if (count == 0)
+               return HOST_NOT_FOUND; /* shouldn't happen? */
+
+       /* if there are multiple addresses, return random one */
        ip4->family = AF_INET;
-       memcpy(&ip4->ip, hp->h_addr, 4);
+       memcpy(&ip4->ip, hp->h_addr_list[rand() % count], 4);
 
        return 0;
 #endif
@@ -466,10 +485,9 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
 int net_gethostbyaddr(IPADDR *ip, char **name)
 {
 #ifdef HAVE_IPV6
-       struct addrinfo req, *ai;
+       union sockaddr_union so;
        int host_error;
        char hostname[NI_MAXHOST];
-       char ipname[MAX_IP_LEN];
 #else
        struct hostent *hp;
 #endif
@@ -479,29 +497,19 @@ int net_gethostbyaddr(IPADDR *ip, char **name)
 
        *name = NULL;
 #ifdef HAVE_IPV6
-       net_ip2host(ip, ipname);
-
-       memset(&req, 0, sizeof(struct addrinfo));
-       req.ai_socktype = SOCK_STREAM;
-       req.ai_flags = AI_CANONNAME;
+       memset(&so, 0, sizeof(so));
+       sin_set_ip(&so, ip);
 
        /* save error to host_error for later use */
-       host_error = getaddrinfo(ipname, NULL, &req, &ai);
-       if (host_error != 0)
-               return host_error;
-        host_error = getnameinfo(ai->ai_addr, ai->ai_addrlen,
-                                 hostname, NI_MAXHOST, NULL, 0, 0);
-        if (host_error != 0) {
-                freeaddrinfo(ai);
+        host_error = getnameinfo((struct sockaddr *) &so, sizeof(so),
+                                 hostname, sizeof(hostname), NULL, 0, 0);
+        if (host_error != 0)
                 return host_error;
-        }
 
        *name = g_strdup(hostname);
-
-       freeaddrinfo(ai);
 #else
        if (ip->family != AF_INET) return -1;
-       hp = gethostbyaddr(&ip->ip, 4, AF_INET);
+       hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET);
        if (hp == NULL) return -1;
 
        *name = g_strdup(hp->h_name);
@@ -611,7 +619,11 @@ const char *net_gethosterror(int error)
 int net_hosterror_notfound(int error)
 {
 #ifdef HAVE_IPV6
+#ifdef EAI_NODATA /* NODATA is depricated */
        return error != 1 && (error == EAI_NONAME || error == EAI_NODATA);
+#else
+       return error != 1 && (error == EAI_NONAME);
+#endif
 #else
        return error == HOST_NOT_FOUND || error == NO_ADDRESS;
 #endif
index c6b08f9fe48d6cb830a5089c27de217c245b19c7..5e445e8383603eb5864d0213f2b005746ee84624 100644 (file)
@@ -39,13 +39,15 @@ struct _IPADDR {
 
 #define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
 
+extern IPADDR ip4_any;
+
 /* returns 1 if IPADDRs are the same */
 int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
 
 /* Connect to socket */
 GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
 /* Connect to socket with ip address and SSL*/
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip);
+GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify);
 /* Connect to socket with ip address */
 GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
 /* Connect to named UNIX socket */
index d0dbce3dacf96cab9994abd5a83ccf56fbaa4da7..aaedded98b568c6cee9993d5da50e0563c3c985f 100644 (file)
@@ -19,6 +19,7 @@ unsigned int send_massjoin:1; /* Waiting to be sent in massjoin signal */
 unsigned int op:1;
 unsigned int halfop:1;
 unsigned int voice:1;
+char other;
 
 /*GHashTable *module_data;*/
 
index 7b97cb8ef67ebdf25c45ab0cc9767a45b8390418..4f322bf55a5dd225030d3a473b0b0f3a1e75a2ef 100644 (file)
@@ -356,8 +356,16 @@ GSList *nicklist_get_same_unique(SERVER_REC *server, void *id)
        return rec.list;
 }
 
+#if GLIB_MAJOR_VERSION < 2
+/* glib1 doesn't have g_slist_sort_with_data, so non-standard prefixes won't be sorted correctly */
+int nicklist_compare_glib1(NICK_REC *p1, NICK_REC *p2)
+{
+       return nicklist_compare(p1, p2, NULL);
+}
+#endif
+
 /* nick record comparision for sort functions */
-int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
+int nicklist_compare(NICK_REC *p1, NICK_REC *p2, const char *nick_prefix)
 {
        int status1, status2;
        
@@ -369,7 +377,10 @@ int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
         * returns :-)
         * -- yath */
 
-       if (p1->op)
+       if (p1->other) {
+               const char *other = (nick_prefix == NULL) ? NULL : strchr(nick_prefix, p1->other);
+               status1 = (other == NULL) ? 5 : 1000 - (other - nick_prefix);
+       } else if (p1->op)
                status1 = 4;
        else if (p1->halfop)
                status1 = 3;
@@ -378,7 +389,10 @@ int nicklist_compare(NICK_REC *p1, NICK_REC *p2)
        else
                status1 = 1;
 
-       if (p2->op)
+       if (p2->other) {
+               const char *other = (nick_prefix == NULL) ? NULL : strchr(nick_prefix, p2->other);
+               status2 = (other == NULL) ? 5 : 1000 - (other - nick_prefix);
+       } else if (p2->op)
                status2 = 4;
        else if (p2->halfop)
                status2 = 3;
index a96835002e60b099053640bf3d8b01ce52401722..78dabcc009bca2837f07884c0adeab4fc1ddfb84 100644 (file)
@@ -49,7 +49,10 @@ void nicklist_update_flags_unique(SERVER_REC *server, void *id,
 void nicklist_set_own(CHANNEL_REC *channel, NICK_REC *nick);
 
 /* Nick record comparision for sort functions */
-int nicklist_compare(NICK_REC *p1, NICK_REC *p2);
+#if GLIB_MAJOR_VERSION < 2
+int nicklist_compare_glib1(NICK_REC *p1, NICK_REC *p2);
+#endif
+int nicklist_compare(NICK_REC *p1, NICK_REC *p2, const char *nick_prefix);
 
 /* Check is `msg' is meant for `nick'. */
 int nick_match_msg(CHANNEL_REC *channel, const char *msg, const char *nick);
diff --git a/apps/irssi/src/core/recode.c b/apps/irssi/src/core/recode.c
new file mode 100644 (file)
index 0000000..aadc09b
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ recode.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 "settings.h"
+#include "servers.h"
+#include "signals.h"
+#include "lib-config/iconfig.h"
+#include "misc.h"
+
+static gboolean recode_get_charset(const char **charset)
+{
+       *charset = settings_get_str("term_charset");
+       if (**charset)
+               /* we use the same test as in src/fe-text/term.c:123 */
+               return (g_strcasecmp(*charset, "utf-8") == 0);
+
+#ifdef HAVE_GLIB2
+       return g_get_charset(charset);
+#else
+       return FALSE;
+#endif
+}
+
+gboolean is_utf8(void)
+{
+       const char *charset;
+
+       return recode_get_charset(&charset);
+}
+
+#ifdef HAVE_GLIB2
+static gboolean is_translit(const char *charset)
+{
+       char *pos;
+
+       pos = stristr(charset, "//translit");
+       return (pos != NULL);
+}
+#endif
+
+gboolean is_valid_charset(const char *charset)
+{
+#ifdef HAVE_GLIB2
+       const char *from="UTF-8";
+       const char *str="irssi";
+       char *recoded, *to = NULL;
+       gboolean valid;
+
+       if (!charset || *charset == '\0')
+               return FALSE;
+
+       if (settings_get_bool("recode_transliterate") && !is_translit(charset))
+               charset = to = g_strconcat(charset, "//TRANSLIT", NULL);
+
+       recoded = g_convert(str, strlen(str), charset, from, NULL, NULL, NULL);
+       valid = (recoded != NULL);
+       g_free(recoded);
+       g_free(to);
+       return valid;
+#else
+       if (!charset || *charset =='\0')
+               return FALSE;
+       return TRUE;
+#endif
+}
+
+char *recode_in(const SERVER_REC *server, const char *str, const char *target)
+{
+#ifdef HAVE_GLIB2
+       const char *from = NULL;
+       const char *to = NULL;
+       char *translit_to = NULL;
+       char *recoded = NULL;
+       gboolean term_is_utf8, str_is_utf8, translit, recode, autodetect;
+       int len;
+       int i;
+
+       if (!str)
+               return NULL;
+
+       recode = settings_get_bool("recode");
+       if (!recode)
+               return g_strdup(str);
+
+       len = strlen(str);
+
+       /* Only validate for UTF-8 if an 8-bit encoding. */
+       str_is_utf8 = 0;
+       for (i = 0; i < len; ++i) {
+               if (str[i] & 0x80) {
+                       str_is_utf8 = g_utf8_validate(str, len, NULL);
+                       break;
+               }
+       }
+       translit = settings_get_bool("recode_transliterate");
+       autodetect = settings_get_bool("recode_autodetect_utf8");
+       term_is_utf8 = recode_get_charset(&to);
+
+       if (autodetect && str_is_utf8)
+               if (term_is_utf8)
+                       return g_strdup(str);
+               else
+                       from = "UTF-8";
+                       
+       else {
+               if (server != NULL && server->tag != NULL && target != NULL) {
+                       char *tagtarget = g_strdup_printf("%s/%s", server->tag, target);
+                       from = iconfig_get_str("conversions", tagtarget, NULL);
+                       g_free(tagtarget);
+               }
+
+               if (target != NULL && from == NULL)
+                       from = iconfig_get_str("conversions", target, NULL);
+
+               if (from == NULL && server != NULL)
+                       from = iconfig_get_str("conversions", server->tag, NULL);
+
+       }
+
+       if (translit && !is_translit(to))
+               to = translit_to = g_strconcat(to, "//TRANSLIT", NULL);
+               
+       if (from)
+               recoded = g_convert_with_fallback(str, len, to, from, NULL, NULL, NULL, NULL);
+
+       if (!recoded) {
+               if (term_is_utf8) {
+                       if (!str_is_utf8)
+                               from = settings_get_str("recode_fallback");
+
+               } else if (str_is_utf8)
+                       from = "UTF-8";
+
+               if (from)
+                       recoded = g_convert_with_fallback(str, len, to, from, NULL, NULL, NULL, NULL);
+
+               if (!recoded)
+                       recoded = g_strdup(str);
+       }
+       g_free(translit_to);
+       return recoded;
+#else
+       return g_strdup(str);
+#endif
+}
+
+char *recode_out(const SERVER_REC *server, const char *str, const char *target)
+{
+#ifdef HAVE_GLIB2
+       char *recoded = NULL;
+       const char *from = NULL;
+       const char *to = NULL;
+       char *translit_to = NULL;
+       gboolean translit, term_is_utf8, recode;
+       int len;
+
+       if (!str)
+               return NULL;
+
+       recode = settings_get_bool("recode");
+       if (!recode)
+               return g_strdup(str);
+
+       len = strlen(str);
+
+       translit = settings_get_bool("recode_transliterate");
+
+       if (server != NULL && server->tag != NULL && target != NULL) {
+               char *tagtarget = g_strdup_printf("%s/%s", server->tag, target);
+               to = iconfig_get_str("conversions", tagtarget, NULL);
+               g_free(tagtarget);
+       }
+       if (to == NULL || *to == '\0')
+               to = iconfig_get_str("conversions", target, NULL);
+       if ((to == NULL || *to == '\0') && server != NULL)
+               to = iconfig_get_str("conversions", server->tag, NULL);
+       if (to == NULL || *to == '\0')
+               /* default outgoing charset if set */
+               to = settings_get_str("recode_out_default_charset");
+
+       if (to && *to != '\0') {
+               if (translit && !is_translit(to))
+                       to = translit_to = g_strconcat(to ,"//TRANSLIT", NULL);
+
+               term_is_utf8 = recode_get_charset(&from);
+               recoded = g_convert(str, len, to, from, NULL, NULL, NULL);
+       }
+       g_free(translit_to);
+       if (!recoded)
+               recoded = g_strdup(str);
+
+       return recoded;
+#else
+       return g_strdup(str);
+#endif
+}
+
+void recode_init(void)
+{
+       settings_add_bool("misc", "recode", TRUE);
+       settings_add_str("misc", "recode_fallback", "CP1252");
+       settings_add_str("misc", "recode_out_default_charset", "");
+       settings_add_bool("misc", "recode_transliterate", TRUE);
+       settings_add_bool("misc", "recode_autodetect_utf8", TRUE);
+}
+
+void recode_deinit(void)
+{      
+
+}
diff --git a/apps/irssi/src/core/recode.h b/apps/irssi/src/core/recode.h
new file mode 100644 (file)
index 0000000..9698ae2
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __RECODE_H
+#define __RECODE_H
+
+char *recode_in (const SERVER_REC *server, const char *str, const char *target);
+char *recode_out (const SERVER_REC *server, const char *str, const char *target);
+gboolean is_valid_charset(const char *charset);
+gboolean is_utf8(void);
+
+void recode_init (void);
+void recode_deinit (void);
+
+#endif /* __RECODE_H */
index ce1e48a4fd3459b095c7b1ba535a9ec1c23ee8d3..cfbe3eba12946c166b5041abdc5c864c9001e8f3 100644 (file)
@@ -23,12 +23,20 @@ char *nick;
 char *username;
 char *realname;
 
+char *ssl_cert;
+char *ssl_pkey;
+char *ssl_cafile;
+char *ssl_capath;
+
 GIOChannel *connect_handle; /* connect using this handle */
 
 /* when reconnecting, the old server status */
-unsigned int reconnection:1; /* we're trying to reconnect */
+unsigned int reconnection:1; /* we're trying to reconnect a connected server */
+unsigned int reconnecting:1; /* we're trying to reconnect any connection */
 unsigned int no_autojoin_channels:1; /* don't autojoin any channels */
 unsigned int unix_socket:1; /* Connect using named unix socket */
 unsigned int use_ssl:1; /* this connection uses SSL */
+unsigned int ssl_verify:1;
+unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */
 char *channels;
 char *away_reason;
index 0003f698138cbdd500996e5011448dc0a811ee06..bc8124dcfb3a948558378e1d10c981f53892ce59 100644 (file)
@@ -53,12 +53,12 @@ GSList *queries;
    channel keys etc. */
 void (*channels_join)(SERVER_REC *server, const char *data, int automatic);
 /* returns true if `flag' indicates a nick flag (op/voice/halfop) */
-int (*isnickflag)(char flag);
+int (*isnickflag)(SERVER_REC *server, char flag);
 /* returns true if `data' indicates a channel */
 int (*ischannel)(SERVER_REC *server, const char *data);
 /* returns all nick flag characters in order op, voice, halfop. If some
    of them aren't supported '\0' can be used. */
-const char *(*get_nick_flags)(void);
+const char *(*get_nick_flags)(SERVER_REC *server);
 /* send public or private message to server */
 void (*send_message)(SERVER_REC *server, const char *target,
                     const char *msg, int target_type);
index d04fa1fea9112500e25f2e75967c384bf4480046..b7a0c80d055436352cb90bbf398a15a36f019750 100644 (file)
@@ -8,6 +8,11 @@ char *address;
 int port;
 char *password;
 
+char *ssl_cert;
+char *ssl_pkey;
+char *ssl_cafile;
+char *ssl_capath;
+
 char *own_host; /* address to use when connecting this server */
 IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
 
@@ -19,5 +24,6 @@ unsigned int last_failed:1; /* if last connection attempt failed */
 unsigned int banned:1; /* if we're banned from this server */
 unsigned int dns_error:1; /* DNS said the host doesn't exist */
 unsigned int use_ssl:1; /* this connection uses SSL */
+unsigned int ssl_verify:1;
 
 GHashTable *module_data;
index c7b5dd45291c960bec1386833dd619e4cf0e4651..5f53368c70900215214c411f64145df9d9092b1d 100644 (file)
@@ -71,6 +71,7 @@ static void server_reconnect_add(SERVER_CONNECT_REC *conn,
        rec->next_connect = next_connect;
 
        rec->conn = conn;
+       conn->reconnecting = TRUE;
        server_connect_ref(conn);
 
        reconnects = g_slist_append(reconnects, rec);
@@ -191,6 +192,11 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
         dest->no_autojoin_channels = src->no_autojoin_channels;
 
        dest->use_ssl = src->use_ssl;
+       dest->ssl_cert = g_strdup(src->ssl_cert);
+       dest->ssl_pkey = g_strdup(src->ssl_pkey);
+       dest->ssl_verify = src->ssl_verify;
+       dest->ssl_cafile = g_strdup(src->ssl_cafile);
+       dest->ssl_capath = g_strdup(src->ssl_capath);
 
        return dest;
 }
@@ -407,7 +413,7 @@ static void cmd_reconnect(const char *data, SERVER_REC *server)
        if (*data == '\0') {
                /* reconnect to first server in reconnection list */
                if (reconnects == NULL)
-                       cmd_return_error(CMDERR_NOT_CONNECTED);
+                       cmd_param_error(CMDERR_NOT_CONNECTED);
                 rec = reconnects->data;
        } else {
                if (g_strncasecmp(data, "RECON-", 6) == 0)
@@ -461,14 +467,14 @@ static void sig_chat_protocol_deinit(CHAT_PROTOCOL_REC *proto)
 
 static void read_settings(void)
 {
-       reconnect_time = settings_get_int("server_reconnect_time");
-        connect_timeout = settings_get_int("server_connect_timeout");
+       reconnect_time = settings_get_time("server_reconnect_time")/1000;
+        connect_timeout = settings_get_time("server_connect_timeout")/1000;
 }
 
 void servers_reconnect_init(void)
 {
-       settings_add_int("server", "server_reconnect_time", 300);
-       settings_add_int("server", "server_connect_timeout", 300);
+       settings_add_time("server", "server_reconnect_time", "5min");
+       settings_add_time("server", "server_connect_timeout", "5min");
 
        reconnects = NULL;
        last_reconnect_tag = 0;
index 6bdafb80747f06de055e176080fb844c4646cfe6..2aeb1d5d2b99cc58f69530a38783d8f5f257e975 100644 (file)
@@ -163,7 +163,17 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
                 conn->family = sserver->family;
        if (sserver->port > 0 && conn->port <= 0)
                conn->port = sserver->port;
+
        conn->use_ssl = sserver->use_ssl;
+       if (conn->ssl_cert == NULL && sserver->ssl_cert != NULL && sserver->ssl_cert[0] != '\0')
+               conn->ssl_cert = g_strdup(sserver->ssl_cert);
+       if (conn->ssl_pkey == NULL && sserver->ssl_pkey != NULL && sserver->ssl_pkey[0] != '\0')
+               conn->ssl_pkey = g_strdup(sserver->ssl_pkey);
+       conn->ssl_verify = sserver->ssl_verify;
+       if (conn->ssl_cafile == NULL && sserver->ssl_cafile != NULL && sserver->ssl_cafile[0] != '\0')
+               conn->ssl_cafile = g_strdup(sserver->ssl_cafile);
+       if (conn->ssl_capath == NULL && sserver->ssl_capath != NULL && sserver->ssl_capath[0] != '\0')
+               conn->ssl_capath = g_strdup(sserver->ssl_capath);
 
        server_setup_fill_reconn(conn, sserver);
 
@@ -394,6 +404,15 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
        rec->address = g_strdup(server);
        rec->password = g_strdup(config_node_get_str(node, "password", NULL));
        rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE);
+       rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL));
+       rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL));
+       rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE);
+       rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL));
+       rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL));
+       if (rec->ssl_cafile || rec->ssl_capath)
+               rec->ssl_verify = TRUE;
+       if (rec->ssl_cert != NULL || rec->ssl_verify)
+               rec->use_ssl = TRUE;
        rec->port = port;
        rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
        rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE);
@@ -424,6 +443,11 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
        iconfig_node_set_int(node, "port", rec->port);
        iconfig_node_set_str(node, "password", rec->password);
        iconfig_node_set_bool(node, "use_ssl", rec->use_ssl);
+       iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert);
+       iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey);
+       iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify);
+       iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile);
+       iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath);
        iconfig_node_set_str(node, "own_host", rec->own_host);
 
        iconfig_node_set_str(node, "family",
@@ -460,6 +484,10 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec)
        g_free_not_null(rec->own_ip6);
        g_free_not_null(rec->chatnet);
        g_free_not_null(rec->password);
+       g_free_not_null(rec->ssl_cert);
+       g_free_not_null(rec->ssl_pkey);
+       g_free_not_null(rec->ssl_cafile);
+       g_free_not_null(rec->ssl_capath);
        g_free(rec->address);
        g_free(rec);
 }
@@ -517,6 +545,13 @@ void servers_setup_init(void)
        settings_add_str("server", "user_name", NULL);
        settings_add_str("server", "real_name", NULL);
 
+       settings_add_bool("server", "use_ssl", FALSE);
+       settings_add_str("server", "ssl_cert", NULL);
+       settings_add_str("server", "ssl_pkey", NULL);
+       settings_add_bool("server", "ssl_verify", FALSE);
+       settings_add_str("server", "ssl_cafile", NULL);
+       settings_add_str("server", "ssl_cacert", NULL);
+
        settings_add_bool("proxy", "use_proxy", FALSE);
        settings_add_str("proxy", "proxy_address", "");
        settings_add_int("proxy", "proxy_port", 6667);
index f9ab791aabb1f6e1666c1584aa1dcf47dc5d2691..ca18914ae79259994f62faaf00f3f6e4cab23ee6 100644 (file)
@@ -171,13 +171,19 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
                                const char *unix_socket)
 {
        GIOChannel *handle;
-        IPADDR *own_ip;
+       IPADDR *own_ip = NULL;
+       const char *errmsg;
+       char *errmsg2;
+       char ipaddr[MAX_IP_LEN];
         int port;
 
        g_return_if_fail(ip != NULL || unix_socket != NULL);
 
        signal_emit("server connecting", 2, server, ip);
 
+       if (server->connrec->no_connect)
+               return;
+
        if (ip != NULL) {
                own_ip = ip == NULL ? NULL :
                        (IPADDR_IS_V6(ip) ? server->connrec->own_ip6 :
@@ -185,7 +191,8 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
                port = server->connrec->proxy != NULL ?
                        server->connrec->proxy_port : server->connrec->port;
                handle = server->connrec->use_ssl ?
-                       net_connect_ip_ssl(ip, port, own_ip) :
+                       net_connect_ip_ssl(ip, port, own_ip, server->connrec->ssl_cert, server->connrec->ssl_pkey,
+server->connrec->ssl_cafile, server->connrec->ssl_capath, server->connrec->ssl_verify) :
                        net_connect_ip(ip, port, own_ip);
        } else {
                handle = net_connect_unix(unix_socket);
@@ -193,11 +200,22 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip,
 
        if (handle == NULL) {
                /* failed */
+               errmsg = g_strerror(errno);
+               errmsg2 = NULL;
+               if (errno == EADDRNOTAVAIL) {
+                       if (own_ip != NULL) {
+                               /* show the IP which is causing the error */
+                               net_ip2host(own_ip, ipaddr);
+                               errmsg2 = g_strconcat(errmsg, ": ", ipaddr, NULL);
+                       }
+                       server->no_reconnect = TRUE;
+               }
                if (server->connrec->use_ssl && errno == ENOSYS)
                        server->no_reconnect = TRUE;
 
                server->connection_lost = TRUE;
-               server_connect_failed(server, g_strerror(errno));
+               server_connect_failed(server, errmsg2 ? errmsg2 : errmsg);
+               g_free(errmsg2);
        } else {
                server->handle = net_sendbuffer_create(handle, 0);
                server->connect_tag =
@@ -580,6 +598,11 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
         g_free_not_null(conn->username);
        g_free_not_null(conn->realname);
 
+       g_free_not_null(conn->ssl_cert);
+       g_free_not_null(conn->ssl_pkey);
+       g_free_not_null(conn->ssl_cafile);
+       g_free_not_null(conn->ssl_capath);
+
        g_free_not_null(conn->channels);
         g_free_not_null(conn->away_reason);
 
index 40c4f5474938848d41100f3e2a4f75b8d67afd93..f3553634ba7f01841cbd34bdb4dc2a1c49b3f990 100644 (file)
 #include "nicklist.h"
 
 static char *session_file;
-static char *irssi_binary;
+char *irssi_binary = NULL;
 
 static char **session_args;
 
-void session_set_binary(const char *path)
+#ifndef HAVE_GLIB2
+static char *g_find_program_in_path(const char *path)
 {
        const char *envpath;
        char **paths, **tmp;
         char *str;
-
-       g_free_and_null(irssi_binary);
+       char *result = NULL;
 
        if (g_path_is_absolute(path)) {
                 /* full path - easy */
-               irssi_binary = g_strdup(path);
-                return;
+               if(access(path, X_OK) == -1)
+                       return NULL;
+               else
+                       return g_strdup(path);
        }
 
        if (strchr(path, G_DIR_SEPARATOR) != NULL) {
                /* relative path */
                 str = g_get_current_dir();
-               irssi_binary = g_strconcat(str, G_DIR_SEPARATOR_S, path, NULL);
+               result = g_strconcat(str, G_DIR_SEPARATOR_S, path, NULL);
                g_free(str);
-                return;
+               if (access(result, X_OK) == -1) {
+                       g_free(result);
+                       return NULL;
+               }
+               else
+                       return result;
        }
 
        /* we'll need to find it from path. */
        envpath = g_getenv("PATH");
-       if (envpath == NULL) return;
+       if (envpath == NULL) return NULL;
 
        paths = g_strsplit(envpath, ":", -1);
        for (tmp = paths; *tmp != NULL; tmp++) {
                 str = g_strconcat(*tmp, G_DIR_SEPARATOR_S, path, NULL);
                if (access(str, X_OK) == 0) {
-                       irssi_binary = str;
+                       result = str;
                         break;
                }
                 g_free(str);
        }
        g_strfreev(paths);
+
+       return result;
+}
+#endif
+
+void session_set_binary(const char *path)
+{
+       g_free_and_null(irssi_binary);
+
+       irssi_binary = g_find_program_in_path(path);
 }
 
 void session_upgrade(void)
@@ -80,7 +97,7 @@ void session_upgrade(void)
        if (session_args == NULL)
                 return;
 
-       execvp(session_args[0], session_args);
+       execv(session_args[0], session_args);
        fprintf(stderr, "exec failed: %s: %s\n",
                session_args[0], g_strerror(errno));
 }
@@ -90,11 +107,13 @@ static void cmd_upgrade(const char *data)
 {
        CONFIG_REC *session;
        char *session_file, *str;
+       char *binary;
 
        if (*data == '\0')
                data = irssi_binary;
-       if (data == NULL)
-                cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       if ((binary = g_find_program_in_path(data)) == NULL)
+               cmd_return_error(CMDERR_PROGRAM_NOT_FOUND);
 
        /* save the session */
         session_file = g_strdup_printf("%s/session", get_irssi_dir());
@@ -108,7 +127,9 @@ static void cmd_upgrade(const char *data)
        /* data may contain some other program as well, like
           /UPGRADE /usr/bin/screen irssi */
        str = g_strdup_printf("%s --noconnect --session=%s --home=%s --config=%s",
-                             data, session_file, get_irssi_dir(), get_irssi_config());
+                             binary, session_file, get_irssi_dir(), get_irssi_config());
+       g_free(binary);
+       g_free(session_file);
         session_args = g_strsplit(str, " ", -1);
         g_free(str);
 
@@ -118,12 +139,17 @@ static void cmd_upgrade(const char *data)
 static void session_save_nick(CHANNEL_REC *channel, NICK_REC *nick,
                              CONFIG_REC *config, CONFIG_NODE *node)
 {
+       static char other[2];
        node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
 
        config_node_set_str(config, node, "nick", nick->nick);
        config_node_set_bool(config, node, "op", nick->op);
        config_node_set_bool(config, node, "halfop", nick->halfop);
        config_node_set_bool(config, node, "voice", nick->voice);
+       
+       other[0] = nick->other;
+       other[1] = '\0';
+       config_node_set_str(config, node, "other", other);
 
        signal_emit("session save nick", 4, channel, nick, config, node);
 }
@@ -181,8 +207,14 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
        config_node_set_str(config, node, "chatnet", server->connrec->chatnet);
        config_node_set_str(config, node, "password", server->connrec->password);
        config_node_set_str(config, node, "nick", server->nick);
+       config_node_set_str(config, node, "version", server->version);
 
        config_node_set_bool(config, node, "use_ssl", server->connrec->use_ssl);
+       config_node_set_str(config, node, "ssl_cert", server->connrec->ssl_cert);
+       config_node_set_str(config, node, "ssl_pkey", server->connrec->ssl_pkey);
+       config_node_set_bool(config, node, "ssl_verify", server->connrec->ssl_verify);
+       config_node_set_str(config, node, "ssl_cafile", server->connrec->ssl_cafile);
+       config_node_set_str(config, node, "ssl_capath", server->connrec->ssl_capath);
 
        handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
        config_node_set_int(config, node, "handle", handle);
@@ -281,6 +313,7 @@ static void session_restore_server(CONFIG_NODE *node)
                conn->connect_handle = g_io_channel_unix_new(handle);
 
                server = proto->server_init_connect(conn);
+               server->version = g_strdup(config_node_get_str(node, "version", NULL));         
                server->session_reconnect = TRUE;
                signal_emit("session restore server", 2, server, node);
 
@@ -350,16 +383,14 @@ static void sig_init_finished(void)
 void session_init(void)
 {
        static struct poptOption options[] = {
-#if 0 /* --session is not available in SILC Client */
                { "session", 0, POPT_ARG_STRING, &session_file, 0, "Used by /UPGRADE command", "PATH" },
-#endif
                { NULL, '\0', 0, NULL }
        };
 
-        session_file = NULL;
+       session_file = NULL;
        args_register(options);
 
-       /*command_bind("upgrade", NULL, (SIGNAL_FUNC) cmd_upgrade);*/
+       command_bind("upgrade", NULL, (SIGNAL_FUNC) cmd_upgrade);
 
        signal_add("session save", (SIGNAL_FUNC) sig_session_save);
        signal_add("session restore", (SIGNAL_FUNC) sig_session_restore);
@@ -374,7 +405,7 @@ void session_deinit(void)
 {
        g_free_not_null(irssi_binary);
 
-        /*command_unbind("upgrade", (SIGNAL_FUNC) cmd_upgrade);*/
+        command_unbind("upgrade", (SIGNAL_FUNC) cmd_upgrade);
 
        signal_remove("session save", (SIGNAL_FUNC) sig_session_save);
        signal_remove("session restore", (SIGNAL_FUNC) sig_session_restore);
index 5788b5f085c484e6f3da7827aae06763140994d0..000ec56e32bd3d7e326c595d95c4051642599a6c 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef __SESSION_H
 #define __SESSION_H
 
+extern char *irssi_binary;
+
 void session_set_binary(const char *path);
 void session_upgrade(void);
 
index d99757e0a6f0c69ede91359e87b040b0fff1a110..be49d800e2684f961cddbbf176b647d3fd4deeb8 100644 (file)
 #include "module.h"
 #include "signals.h"
 #include "commands.h"
+#include "levels.h"
 #include "misc.h"
 
 #include "lib-config/iconfig.h"
+#include "recode.h"
 #include "settings.h"
 #include "default-config.h"
 
 #include <signal.h>
 
+#define SETTINGS_AUTOSAVE_TIMEOUT (1000*60*60) /* 1 hour */
+
 CONFIG_REC *mainconfig;
 
 static GString *last_errors;
@@ -60,83 +64,109 @@ static SETTINGS_REC *settings_find(const char *key)
        return rec;
 }
 
-const char *settings_get_str(const char *key)
+static SETTINGS_REC *settings_get(const char *key, SettingType type)
 {
        SETTINGS_REC *rec;
-       CONFIG_NODE *setnode, *node;
+
+       g_return_val_if_fail(key != NULL, NULL);
 
        rec = settings_find(key);
-        g_return_val_if_fail(rec != NULL, NULL);
+       if (rec == NULL) {
+               g_warning("settings_get(%s) : not found", key);
+               return NULL;
+       }
+       if (type != -1 && rec->type != type) {
+               g_warning("settings_get(%s) : invalid type", key);
+               return NULL;
+       }
+
+       return rec;
+}
 
-       setnode = iconfig_node_traverse("settings", FALSE);
-       if (setnode == NULL)
-               return rec->def;
+static const char *
+settings_get_str_type(const char *key, SettingType type)
+{
+       SETTINGS_REC *rec;
+       CONFIG_NODE *node;
 
-       node = config_node_section(setnode, rec->module, -1);
-       return node == NULL ? rec->def :
-               config_node_get_str(node, key, rec->def);
+       rec = settings_get(key, type);
+       if (rec == NULL) return NULL;
+
+       node = iconfig_node_traverse("settings", FALSE);
+       node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
+
+       return node == NULL ? rec->default_value.v_string :
+               config_node_get_str(node, key, rec->default_value.v_string);
+}
+
+const char *settings_get_str(const char *key)
+{
+       return settings_get_str_type(key, -1);
 }
 
 int settings_get_int(const char *key)
 {
        SETTINGS_REC *rec;
-       CONFIG_NODE *setnode, *node;
-        int def;
+       CONFIG_NODE *node;
 
-       rec = settings_find(key);
-        g_return_val_if_fail(rec != NULL, 0);
-        def = GPOINTER_TO_INT(rec->def);
+       rec = settings_get(key, SETTING_TYPE_INT);
+       if (rec == NULL) return 0;
 
-       setnode = iconfig_node_traverse("settings", FALSE);
-       if (setnode == NULL)
-               return def;
+       node = iconfig_node_traverse("settings", FALSE);
+       node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
 
-       node = config_node_section(setnode, rec->module, -1);
-       return node == NULL ? def :
-               config_node_get_int(node, key, def);
+       return node == NULL ? rec->default_value.v_int :
+               config_node_get_int(node, key, rec->default_value.v_int);
 }
 
 int settings_get_bool(const char *key)
 {
        SETTINGS_REC *rec;
-       CONFIG_NODE *setnode, *node;
-        int def;
+       CONFIG_NODE *node;
 
-       rec = settings_find(key);
-        g_return_val_if_fail(rec != NULL, 0);
-        def = GPOINTER_TO_INT(rec->def);
+       rec = settings_get(key, SETTING_TYPE_BOOLEAN);
+       if (rec == NULL) return FALSE;
 
-       setnode = iconfig_node_traverse("settings", FALSE);
-       if (setnode == NULL)
-               return def;
+       node = iconfig_node_traverse("settings", FALSE);
+       node = node == NULL ? NULL : config_node_section(node, rec->module, -1);
 
-       node = config_node_section(setnode, rec->module, -1);
-       return node == NULL ? def :
-               config_node_get_bool(node, key, def);
+       return node == NULL ? rec->default_value.v_bool :
+               config_node_get_bool(node, key, rec->default_value.v_bool);
 }
 
-void settings_add_str_module(const char *module, const char *section,
-                            const char *key, const char *def)
+int settings_get_time(const char *key)
 {
-       SETTINGS_REC *rec;
+       const char *str;
+       int msecs;
 
-       g_return_if_fail(key != NULL);
-       g_return_if_fail(section != NULL);
+       str = settings_get_str_type(key, SETTING_TYPE_TIME);
+       if (str != NULL && !parse_time_interval(str, &msecs))
+               g_warning("settings_get_time(%s) : Invalid time '%s'", key, str);
+       return str == NULL ? 0 : msecs;
+}
 
-       rec = g_hash_table_lookup(settings, key);
-       g_return_if_fail(rec == NULL);
+int settings_get_level(const char *key)
+{
+       const char *str;
 
-       rec = g_new0(SETTINGS_REC, 1);
-        rec->module = g_strdup(module);
-       rec->key = g_strdup(key);
-       rec->section = g_strdup(section);
-       rec->def = def == NULL ? NULL : g_strdup(def);
+       str = settings_get_str_type(key, SETTING_TYPE_LEVEL);
+       return str == NULL ? 0 : level2bits(str);
+}
+
+int settings_get_size(const char *key)
+{
+       const char *str;
+       int bytes;
 
-       g_hash_table_insert(settings, rec->key, rec);
+       str = settings_get_str_type(key, SETTING_TYPE_SIZE);
+       if (str != NULL && !parse_size(str, &bytes))
+               g_warning("settings_get_size(%s) : Invalid size '%s'", key, str);
+       return str == NULL ? 0 : bytes;
 }
 
-void settings_add_int_module(const char *module, const char *section,
-                            const char *key, int def)
+static void settings_add(const char *module, const char *section,
+                        const char *key, SettingType type,
+                        const SettingValue *default_value)
 {
        SETTINGS_REC *rec;
 
@@ -144,49 +174,108 @@ void settings_add_int_module(const char *module, const char *section,
        g_return_if_fail(section != NULL);
 
        rec = g_hash_table_lookup(settings, key);
-       g_return_if_fail(rec == NULL);
+       if (rec != NULL) {
+               /* Already exists, make sure it's correct type */
+               if (rec->type != type) {
+                       g_warning("Trying to add already existing "
+                                 "setting '%s' with different type.", key);
+                       return;
+               }
+       } else {
+               rec = g_new(SETTINGS_REC, 1);
+               rec->module = g_strdup(module);
+               rec->key = g_strdup(key);
+               rec->section = g_strdup(section);
+                rec->type = type;
+
+               rec->default_value = *default_value;
+               g_hash_table_insert(settings, rec->key, rec);
+       }
 
-       rec = g_new0(SETTINGS_REC, 1);
-        rec->module = g_strdup(module);
-       rec->type = SETTING_TYPE_INT;
-       rec->key = g_strdup(key);
-       rec->section = g_strdup(section);
-       rec->def = GINT_TO_POINTER(def);
+        rec->refcount++;
+}
+
+void settings_add_str_module(const char *module, const char *section,
+                            const char *key, const char *def)
+{
+       SettingValue default_value;
+
+       memset(&default_value, 0, sizeof(default_value));
+       default_value.v_string = g_strdup(def);
+       settings_add(module, section, key, SETTING_TYPE_STRING, &default_value);
+}
 
-       g_hash_table_insert(settings, rec->key, rec);
+void settings_add_int_module(const char *module, const char *section,
+                            const char *key, int def)
+{
+       SettingValue default_value;
+
+       memset(&default_value, 0, sizeof(default_value));
+        default_value.v_int = def;
+       settings_add(module, section, key, SETTING_TYPE_INT, &default_value);
 }
 
 void settings_add_bool_module(const char *module, const char *section,
                              const char *key, int def)
 {
-       SETTINGS_REC *rec;
+       SettingValue default_value;
 
-       g_return_if_fail(key != NULL);
-       g_return_if_fail(section != NULL);
+       memset(&default_value, 0, sizeof(default_value));
+        default_value.v_bool = def;
+       settings_add(module, section, key, SETTING_TYPE_BOOLEAN,
+                    &default_value);
+}
 
-       rec = g_hash_table_lookup(settings, key);
-       g_return_if_fail(rec == NULL);
+void settings_add_time_module(const char *module, const char *section,
+                             const char *key, const char *def)
+{
+       SettingValue default_value;
+
+       memset(&default_value, 0, sizeof(default_value));
+       default_value.v_string = g_strdup(def);
+       settings_add(module, section, key, SETTING_TYPE_TIME, &default_value);
+}
+
+void settings_add_level_module(const char *module, const char *section,
+                              const char *key, const char *def)
+{
+       SettingValue default_value;
 
-       rec = g_new0(SETTINGS_REC, 1);
-        rec->module = g_strdup(module);
-       rec->type = SETTING_TYPE_BOOLEAN;
-       rec->key = g_strdup(key);
-       rec->section = g_strdup(section);
-       rec->def = GINT_TO_POINTER(def);
+       memset(&default_value, 0, sizeof(default_value));
+       default_value.v_string = g_strdup(def);
+       settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value);
+}
+
+void settings_add_size_module(const char *module, const char *section,
+                             const char *key, const char *def)
+{
+       SettingValue default_value;
 
-       g_hash_table_insert(settings, rec->key, rec);
+       memset(&default_value, 0, sizeof(default_value));
+       default_value.v_string = g_strdup(def);
+       settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value);
 }
 
 static void settings_destroy(SETTINGS_REC *rec)
 {
-       if (rec->type == SETTING_TYPE_STRING)
-               g_free_not_null(rec->def);
+       if (rec->type != SETTING_TYPE_INT &&
+           rec->type != SETTING_TYPE_BOOLEAN)
+               g_free(rec->default_value.v_string);
         g_free(rec->module);
         g_free(rec->section);
         g_free(rec->key);
        g_free(rec);
 }
 
+static void settings_unref(SETTINGS_REC *rec, int remove_hash)
+{
+       if (--rec->refcount == 0) {
+               if (remove_hash)
+                       g_hash_table_remove(settings, rec->key);
+               settings_destroy(rec);
+       }
+}
+
 void settings_remove(const char *key)
 {
        SETTINGS_REC *rec;
@@ -194,17 +283,15 @@ void settings_remove(const char *key)
        g_return_if_fail(key != NULL);
 
        rec = g_hash_table_lookup(settings, key);
-       if (rec == NULL) return;
-
-       g_hash_table_remove(settings, key);
-       settings_destroy(rec);
+       if (rec != NULL)
+               settings_unref(rec, TRUE);
 }
 
 static int settings_remove_hash(const char *key, SETTINGS_REC *rec,
                                const char *module)
 {
        if (strcmp(rec->module, module) == 0) {
-               settings_destroy(rec);
+               settings_unref(rec, FALSE);
                 return TRUE;
        }
 
@@ -226,7 +313,10 @@ static CONFIG_NODE *settings_get_node(const char *key)
        g_return_val_if_fail(key != NULL, NULL);
 
        rec = g_hash_table_lookup(settings, key);
-       g_return_val_if_fail(rec != NULL, NULL);
+       if (rec == NULL) {
+               g_warning("Changing unknown setting '%s'", key);
+               return NULL;
+       }
 
        node = iconfig_node_traverse("settings", TRUE);
        return config_node_section(node, rec->module, NODE_TYPE_BLOCK);
@@ -247,7 +337,35 @@ void settings_set_bool(const char *key, int value)
         iconfig_node_set_bool(settings_get_node(key), key, value);
 }
 
-int settings_get_type(const char *key)
+int settings_set_time(const char *key, const char *value)
+{
+       int msecs;
+
+       if (!parse_time_interval(value, &msecs))
+               return FALSE;
+
+       iconfig_node_set_str(settings_get_node(key), key, value);
+       return TRUE;
+}
+
+int settings_set_level(const char *key, const char *value)
+{
+        iconfig_node_set_str(settings_get_node(key), key, value);
+       return TRUE;
+}
+
+int settings_set_size(const char *key, const char *value)
+{
+       int size;
+
+       if (!parse_size(value, &size))
+               return FALSE;
+
+        iconfig_node_set_str(settings_get_node(key), key, value);
+       return TRUE;
+}
+
+SettingType settings_get_type(const char *key)
 {
        SETTINGS_REC *rec;
 
@@ -276,6 +394,8 @@ static void sig_init_finished(void)
        if (config_changed) {
                /* some backwards compatibility changes were made to
                   config file, reload it */
+               g_warning("Some settings were automatically "
+                         "updated, please /SAVE");
                signal_emit("setup changed", 0);
        }
 }
@@ -311,10 +431,92 @@ void settings_clean_invalid(void)
 
                 settings_clean_invalid_module(module);
 
-                g_free(module);
                last_invalid_modules =
                        g_slist_remove(last_invalid_modules, module);
+                g_free(module);
+       }
+}
+
+static int backwards_compatibility(const char *module, CONFIG_NODE *node,
+                                  CONFIG_NODE *parent)
+{
+       const char *new_key, *new_module;
+       CONFIG_NODE *new_node;
+       char *new_value;
+       int old_value;
+
+       new_value = NULL; new_key = NULL; new_module = NULL;
+
+       /* fe-text term_type -> fe-common/core term_charset - for 0.8.10-> */
+       if (strcmp(module, "fe-text") == 0) {
+               if (strcasecmp(node->key, "term_type") == 0 ||
+                   /* kludge for cvs-version where term_charset was in fe-text */
+                   strcasecmp(node->key, "term_charset") == 0) {
+                       new_module = "fe-common/core";
+                       new_key = "term_charset";
+                       new_value = !is_valid_charset(node->value) ? NULL :
+                               g_strdup(node->value);
+                       new_node = iconfig_node_traverse("settings", FALSE);
+                       new_node = new_node == NULL ? NULL :
+                               config_node_section(new_node, new_module, -1);
+
+                       config_node_set_str(mainconfig, new_node,
+                                           new_key, new_value);
+                       /* remove old */
+                       config_node_set_str(mainconfig, parent,
+                                           node->key, NULL);
+                       g_free(new_value);
+                       config_changed = TRUE;
+                       return new_key != NULL;
+               }
+       }
+       new_value = NULL, new_key = NULL;
+       /* FIXME: remove later - for 0.8.6 -> */
+       if (node->value == NULL || !is_numeric(node->value, '\0'))
+               return FALSE;
+
+       old_value = atoi(node->value);
+
+       if (strcmp(module, "fe-text") == 0) {
+               if (strcasecmp(node->key, "lag_min_show") == 0)
+                       new_value = g_strdup_printf("%dms", old_value*10);
+               else if (strcasecmp(node->key, "scrollback_hours") == 0) {
+                       new_value = g_strdup_printf("%dh", old_value);
+                       new_key = "scrollback_time";
+               }
+       } else if (strcmp(module, "irc/core") == 0) {
+               if (strcasecmp(node->key, "cmd_queue_speed") == 0)
+                       new_value = g_strdup_printf("%dms", old_value);
+       } else if (strcmp(module, "irc/dcc") == 0) {
+               if (strcasecmp(node->key, "dcc_autoget_max_size") == 0)
+                       new_value = g_strdup_printf("%dk", old_value);
+       } else if (strcmp(module, "irc/notify") == 0) {
+               if (strcasecmp(node->key, "notify_idle_time") == 0)
+                       new_value = g_strdup_printf("%dmin", old_value);
+       } else if (strcmp(module, "core") == 0) {
+               if (strcasecmp(node->key, "write_buffer_mins") == 0) {
+                       new_value = g_strdup_printf("%dmin", old_value);
+                       new_key = "write_buffer_timeout";
+               } else if (strcasecmp(node->key, "write_buffer_kb") == 0) {
+                       new_value = g_strdup_printf("%dk", old_value);
+                       new_key = "write_buffer_size";
+               }
+       }
+
+       if (new_key != NULL || new_value != NULL) {
+               config_node_set_str(mainconfig, parent,
+                                   new_key != NULL ? new_key : node->key,
+                                   new_value != NULL ?
+                                   new_value : node->value);
+               if (new_key != NULL) {
+                       /* remove old */
+                       config_node_set_str(mainconfig, parent,
+                                           node->key, NULL);
+               }
+               config_changed = TRUE;
+               g_free(new_value);
        }
+       return new_key != NULL;
 }
 
 /* verify that all settings in config file for `module' are actually found
@@ -322,9 +524,9 @@ void settings_clean_invalid(void)
 void settings_check_module(const char *module)
 {
         SETTINGS_REC *set;
-       CONFIG_NODE *node;
+       CONFIG_NODE *node, *parent;
         GString *errors;
-       GSList *tmp;
+       GSList *tmp, *next;
         int count;
 
         g_return_if_fail(module != NULL);
@@ -338,11 +540,16 @@ void settings_check_module(const char *module)
                         "file for module %s:", module);
 
         count = 0;
+       parent = node;
        tmp = config_node_first(node->value);
-       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+       for (; tmp != NULL; tmp = next) {
                node = tmp->data;
+               next = config_node_next(tmp);
 
                set = g_hash_table_lookup(settings, node->key);
+               if (backwards_compatibility(module, node, parent))
+                       continue;
+
                if (set == NULL || strcmp(set->module, module) != 0) {
                        g_string_sprintfa(errors, " %s", node->key);
                         count++;
@@ -485,7 +692,7 @@ static CONFIG_REC *parse_configfile(const char *fname)
                config = config_open(NULL, -1);
        }
 
-        if (path != NULL)
+        if (config->fname != NULL)
                config_parse(config);
         else
                config_parse_data(config, default_config, "internal");
@@ -621,7 +828,8 @@ void settings_init(void)
        init_configfile();
 
        settings_add_bool("misc", "settings_autosave", TRUE);
-       timeout_tag = g_timeout_add(1000*60*60, (GSourceFunc) sig_autosave, NULL);
+       timeout_tag = g_timeout_add(SETTINGS_AUTOSAVE_TIMEOUT,
+                                   (GSourceFunc) sig_autosave, NULL);
        signal_add("irssi init finished", (SIGNAL_FUNC) sig_init_finished);
        signal_add("gui exit", (SIGNAL_FUNC) sig_autosave);
 }
index fdff38015e798ef3ed3e243ef9898ef180b2072d..8a712dcebf778c93985244525d8c4a828d2f7e9e 100644 (file)
@@ -1,18 +1,30 @@
 #ifndef __SETTINGS_H
 #define __SETTINGS_H
 
-enum {
+typedef enum {
        SETTING_TYPE_STRING,
        SETTING_TYPE_INT,
-       SETTING_TYPE_BOOLEAN
-};
+       SETTING_TYPE_BOOLEAN,
+       SETTING_TYPE_TIME,
+       SETTING_TYPE_LEVEL,
+       SETTING_TYPE_SIZE
+} SettingType;
 
 typedef struct {
-        char *module;
-       int type;
+       char *v_string;
+       int v_int;
+       unsigned int v_bool:1;
+} SettingValue;
+
+typedef struct {
+       int refcount;
+
+       char *module;
        char *key;
        char *section;
-       void *def;
+
+       SettingType type;
+       SettingValue default_value;
 } SETTINGS_REC;
 
 /* macros for handling the default Irssi configuration */
@@ -40,6 +52,9 @@ extern const char *default_config;
 const char *settings_get_str(const char *key);
 int settings_get_int(const char *key);
 int settings_get_bool(const char *key);
+int settings_get_time(const char *key); /* as milliseconds */
+int settings_get_level(const char *key);
+int settings_get_size(const char *key); /* as bytes */
 
 /* Functions to add/remove settings */
 void settings_add_str_module(const char *module, const char *section,
@@ -48,6 +63,12 @@ void settings_add_int_module(const char *module, const char *section,
                             const char *key, int def);
 void settings_add_bool_module(const char *module, const char *section,
                              const char *key, int def);
+void settings_add_time_module(const char *module, const char *section,
+                             const char *key, const char *def);
+void settings_add_level_module(const char *module, const char *section,
+                              const char *key, const char *def);
+void settings_add_size_module(const char *module, const char *section,
+                             const char *key, const char *def);
 void settings_remove(const char *key);
 void settings_remove_module(const char *module);
 
@@ -57,13 +78,22 @@ void settings_remove_module(const char *module);
        settings_add_int_module(MODULE_NAME, section, key, def)
 #define settings_add_bool(section, key, def) \
        settings_add_bool_module(MODULE_NAME, section, key, def)
+#define settings_add_time(section, key, def) \
+       settings_add_time_module(MODULE_NAME, section, key, def)
+#define settings_add_level(section, key, def) \
+       settings_add_level_module(MODULE_NAME, section, key, def)
+#define settings_add_size(section, key, def) \
+       settings_add_size_module(MODULE_NAME, section, key, def)
 
 void settings_set_str(const char *key, const char *value);
 void settings_set_int(const char *key, int value);
 void settings_set_bool(const char *key, int value);
+int settings_set_time(const char *key, const char *value);
+int settings_set_level(const char *key, const char *value);
+int settings_set_size(const char *key, const char *value);
 
 /* Get the type (SETTING_TYPE_xxx) of `key' */
-int settings_get_type(const char *key);
+SettingType settings_get_type(const char *key);
 /* Get all settings sorted by section. Free the result with g_slist_free() */
 GSList *settings_get_sorted(void);
 /* Get the record of the setting */
index 1c3eef824b4a54ebca451ae99ec71d00a71e9e5b..eec306ae1350b63abb3f6eabde0141e6f99e1b76 100644 (file)
@@ -134,17 +134,14 @@ static int flush_timeout(void)
 
 static void read_settings(void)
 {
-       int msecs;
-
        write_buffer_flush();
 
-       write_buffer_max_blocks = settings_get_int("write_buffer_kb") *
-               1024/BUFFER_BLOCK_SIZE;
+       write_buffer_max_blocks =
+               settings_get_size("write_buffer_size") / BUFFER_BLOCK_SIZE;
 
-       if (settings_get_int("write_buffer_mins") > 0) {
-                msecs = settings_get_int("write_buffer_mins")*60*1000;
+       if (settings_get_time("write_buffer_timeout") > 0) {
                if (timeout_tag == -1) {
-                       timeout_tag = g_timeout_add(msecs,
+                       timeout_tag = g_timeout_add(settings_get_time("write_buffer_timeout"),
                                                    (GSourceFunc) flush_timeout,
                                                    NULL);
                }
@@ -161,8 +158,8 @@ static void cmd_flushbuffer(void)
 
 void write_buffer_init(void)
 {
-       settings_add_int("misc", "write_buffer_mins", 0);
-       settings_add_int("misc", "write_buffer_kb", 0);
+       settings_add_time("misc", "write_buffer_timeout", "0");
+       settings_add_size("misc", "write_buffer_size", "0");
 
        buffers = g_hash_table_new((GHashFunc) g_direct_hash,
                                   (GCompareFunc) g_direct_equal);
index 920e261148ff796253ef25fc319a720a813b4b4d..416ea2fc8756c74a1f8ccaabaf50fd6503f4598a 100644 (file)
@@ -27,11 +27,13 @@ libfe_common_core_a_SOURCES = \
        fe-queries.c \
        fe-server.c \
        fe-settings.c \
+       utf8.c \
        formats.c \
        hilight-text.c \
        keyboard.c \
        module-formats.c \
        printtext.c \
+       fe-recode.c \
        themes.c \
        translation.c \
        window-activity.c \
@@ -51,12 +53,14 @@ noinst_HEADERS = \
        fe-exec.h \
        fe-messages.h \
        fe-queries.h \
+       utf8.h \
        formats.h \
        hilight-text.h \
        keyboard.h \
        module-formats.h \
        module.h \
        printtext.h \
+       fe-recode.h \
        themes.h \
        translation.h \
        window-activity.h \
index 803700de521465f49d22e08de10d20b48b77a15f..dea7360568443d35ead76320055bfa361428f680 100644 (file)
@@ -77,10 +77,21 @@ static void last_msg_dec_owns(GSList *list)
        }
 }
 
+static void last_msg_destroy(GSList **list, LAST_MSG_REC *rec)
+{
+       *list = g_slist_remove(*list, rec);
+
+       g_free(rec->nick);
+       g_free(rec);
+}
+
 static void last_msg_add(GSList **list, const char *nick, int own, int max)
 {
        LAST_MSG_REC *rec;
 
+       if (max <= 0)
+               return;
+
        rec = last_msg_find(*list, nick);
        if (rec != NULL) {
                /* msg already exists, update it */
@@ -93,9 +104,8 @@ static void last_msg_add(GSList **list, const char *nick, int own, int max)
                rec = g_new(LAST_MSG_REC, 1);
                rec->nick = g_strdup(nick);
 
-               if ((int)g_slist_length(*list) == max) {
-                       *list = g_slist_remove(*list,
-                                              g_slist_last(*list)->data);
+               while ((int)g_slist_length(*list) >= max) {
+                       last_msg_destroy(list, g_slist_last(*list)->data);
                }
 
                rec->own = own ? max : 0;
@@ -107,14 +117,6 @@ static void last_msg_add(GSList **list, const char *nick, int own, int max)
        *list = g_slist_prepend(*list, rec);
 }
 
-static void last_msg_destroy(GSList **list, LAST_MSG_REC *rec)
-{
-       *list = g_slist_remove(*list, rec);
-
-       g_free(rec->nick);
-       g_free(rec);
-}
-
 void completion_last_message_add(const char *nick)
 {
        g_return_if_fail(nick != NULL);
@@ -216,7 +218,6 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
                                    const char *target, const char *origtarget)
 {
        g_return_if_fail(server != NULL);
-       g_return_if_fail(target != NULL);
 
        if (target != NULL && query_find(server, target) == NULL)
                SERVER_LAST_MSG_ADD(server, target);
@@ -786,6 +787,36 @@ GList *completion_get_servers(const char *word)
        return list;
 }
 
+GList *completion_get_targets(const char *word)
+{
+       CONFIG_NODE *node;
+       GList *list;
+       GSList *tmp;
+       int len;
+
+       g_return_val_if_fail(word != NULL, NULL);
+
+       len = strlen(word);
+       list = NULL;
+
+       /* get the list of all conversion targets */
+       node = iconfig_node_traverse("conversions", FALSE);
+       tmp = node == NULL ? NULL : config_node_first(node->value);
+       for (; tmp != NULL; tmp = config_node_next(tmp)) {
+               node = tmp->data;
+
+               if (node->type != NODE_TYPE_KEY)
+                       continue;
+
+               if (len != 0 && g_strncasecmp(node->key, word, len) != 0)
+                       continue;
+
+               list = g_list_append(list, g_strdup(node->key));
+       }
+       
+       return list;
+}
+
 static void sig_complete_connect(GList **list, WINDOW_REC *window,
                                 const char *word, const char *line, 
                                 int *want_space)
@@ -879,7 +910,6 @@ static void sig_complete_alias(GList **list, WINDOW_REC *window,
        }
 }
 
-
 static void sig_complete_channel(GList **list, WINDOW_REC *window,
                                 const char *word, const char *line,
                                 int *want_space)
@@ -902,6 +932,27 @@ static void sig_complete_server(GList **list, WINDOW_REC *window,
        if (*list != NULL) signal_stop();
 }
 
+static void sig_complete_target(GList **list, WINDOW_REC *window,
+                               const char *word, const char *line,
+                               int *want_space)
+{
+       const char *definition;
+       
+       g_return_if_fail(list != NULL);
+       g_return_if_fail(word != NULL);
+       g_return_if_fail(line != NULL);
+
+       if (*line != '\0') {
+               if ((definition = iconfig_get_str("conversions", line ,NULL)) != NULL) {
+                       *list = g_list_append(NULL, g_strdup(definition));
+                       signal_stop();
+               }
+       } else {        
+               *list = completion_get_targets(word);
+               if (*list != NULL) signal_stop();
+       }
+}
+
 /* expand \n, \t and \\ */
 static char *expand_escapes(const char *line, SERVER_REC *server,
                            WI_ITEM_REC *item)
@@ -1088,6 +1139,7 @@ void chat_completion_init(void)
        signal_add("complete command window item move", (SIGNAL_FUNC) sig_complete_channel);
        signal_add("complete command server add", (SIGNAL_FUNC) sig_complete_server);
        signal_add("complete command server remove", (SIGNAL_FUNC) sig_complete_server);
+       signal_add("complete command recode remove", (SIGNAL_FUNC) sig_complete_target);
        signal_add("message public", (SIGNAL_FUNC) sig_message_public);
        signal_add("message join", (SIGNAL_FUNC) sig_message_join);
        signal_add("message private", (SIGNAL_FUNC) sig_message_private);
@@ -1124,6 +1176,7 @@ void chat_completion_deinit(void)
        signal_remove("complete command window item move", (SIGNAL_FUNC) sig_complete_channel);
        signal_remove("complete command server add", (SIGNAL_FUNC) sig_complete_server);
        signal_remove("complete command server remove", (SIGNAL_FUNC) sig_complete_server);
+       signal_remove("complete command recode remove", (SIGNAL_FUNC) sig_complete_target);
        signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
        signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
        signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
index 7a9c9ad3f14669cfa9be3ca882b08f3551f6497c..be8b76c5ccb7bb04dffe3e4071ec4d4315546892 100644 (file)
@@ -38,7 +38,7 @@ static int last_want_space, last_line_pos;
         ((c) == ',')
 
 #define isseparator(c) \
-       (i_isspace(c) || isseparator_notspace(c))
+       ((c) == ' ' || isseparator_notspace(c))
 
 void chat_completion_init(void);
 void chat_completion_deinit(void);
@@ -530,7 +530,7 @@ static char *line_get_command(const char *line, char **args, int aliases)
                } else {
                        checkcmd = g_strndup(line, (int) (ptr-line));
 
-                       while (i_isspace(*ptr)) ptr++;
+                       while (*ptr == ' ') ptr++;
                        cmdargs = ptr;
                }
 
index 4ce5abeb2f5701b51c6c9ae327359cd4496e5ac7..6579e8b237985458af4051b245cfc1aa51952cb9 100644 (file)
@@ -110,7 +110,7 @@ static void sig_channel_joined(CHANNEL_REC *channel)
                fe_channels_nicklist(channel, CHANNEL_NICKLIST_FLAG_ALL);
 }
 
-static void cmd_wjoin_pre(const char *data)
+static void cmd_wjoin_pre(const char *data, SERVER_REC *server)
 {
        GHashTable *optlist;
        char *nick;
@@ -121,6 +121,12 @@ static void cmd_wjoin_pre(const char *data)
                            "join", &optlist, &nick))
                 return;
 
+       /* kludge for /join -invite -window if there is no invite */
+       if (g_hash_table_lookup(optlist, "invite") &&
+            server != NULL && server->last_invite == NULL) {
+               cmd_params_free(free_arg);
+               return;
+        }                    
        if (g_hash_table_lookup(optlist, "window") != NULL) {
                signal_add("channel created",
                           (SIGNAL_FUNC) signal_channel_created_curwin);
@@ -132,20 +138,28 @@ static void cmd_join(const char *data, SERVER_REC *server)
 {
        WINDOW_REC *window;
         CHANNEL_REC *channel;
+       GHashTable *optlist;
+       char *channelname;
+       void *free_arg;
 
-        if (strchr(data, ' ') != NULL || strchr(data, ',') != NULL)
-                return;
-
-        channel = channel_find(server, data);
-       if (channel == NULL)
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
+                           PARAM_FLAG_UNKNOWN_OPTIONS,
+                           "join", &optlist, &channelname))
                return;
 
-       /* already joined to channel, set it active */
-        window = window_item_window(channel);
-       if (window != active_win)
-               window_set_active(window);
-
-       window_item_set_active(active_win, (WI_ITEM_REC *) channel);
+       /* -<server tag> */
+       server = cmd_options_get_server("join", optlist, server);
+       
+       channel = channel_find(server, channelname);
+       if (channel != NULL) {
+               /* already joined to channel, set it active */
+               window = window_item_window(channel);
+               if (window != active_win)
+                       window_set_active(window);
+
+               window_item_set_active(active_win, (WI_ITEM_REC *) channel);
+       }
+       cmd_params_free(free_arg);
 }
 
 static void cmd_wjoin_post(const char *data)
@@ -246,7 +260,7 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 }
 
 /* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
-                       <channel> <chatnet> [<password>] */
+                       <channel> <network> [<password>] */
 static void cmd_channel_add(const char *data)
 {
        GHashTable *optlist;
@@ -298,7 +312,7 @@ static void cmd_channel_add(const char *data)
        cmd_params_free(free_arg);
 }
 
-/* SYNTAX: CHANNEL REMOVE <channel> <chatnet> */
+/* SYNTAX: CHANNEL REMOVE <channel> <network> */
 static void cmd_channel_remove(const char *data)
 {
        CHANNEL_SETUP_REC *rec;
@@ -399,7 +413,9 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
        for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
                NICK_REC *rec = tmp->data;
 
-               if (rec->op)
+               if (rec->other)
+                       nickmode[0] = rec->other;
+               else if (rec->op)
                        nickmode[0] = '@';
                else if (rec->halfop)
                        nickmode[0] = '%';
@@ -457,12 +473,14 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
        NICK_REC *nick;
        GSList *tmp, *nicklist, *sorted;
        int nicks, normal, voices, halfops, ops;
+       const char *nick_flags;
 
        nicks = normal = voices = halfops = ops = 0;
        nicklist = nicklist_getnicks(channel);
        sorted = NULL;
+       nick_flags = channel->server->get_nick_flags(channel->server);
 
-       /* sort the nicklist */
+       /* filter (for flags) and count ops, halfops, voices */
        for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
                nick = tmp->data;
 
@@ -485,11 +503,18 @@ void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
                                continue;
                }
 
-               sorted = g_slist_insert_sorted(sorted, nick, (GCompareFunc)
-                                              nicklist_compare);
+               sorted = g_slist_prepend(sorted, nick);
        }
        g_slist_free(nicklist);
 
+       /* sort the nicklist */
+#if GLIB_MAJOR_VERSION < 2
+       /* glib1 doesn't have g_slist_sort_with_data, so non-standard prefixes won't be sorted correctly */
+       sorted = g_slist_sort(sorted, (GCompareFunc)nicklist_compare_glib1);
+#else
+       sorted = g_slist_sort_with_data(sorted, (GCompareDataFunc) nicklist_compare, (void *)nick_flags);
+#endif
+
        /* display the nicks */
         if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) {
                printformat(channel->server, channel->visible_name,
index bc556a6a5db81fc1b2fc208e0757e69c84a4a810..be42812f96f4550a0fca7cd264dd63579a4e7e0d 100644 (file)
 #include "levels.h"
 #include "settings.h"
 #include "irssi-version.h"
+#ifdef HAVE_NL_LANGINFO
+#  include <langinfo.h>
+#endif
 
 #include "servers.h"
 #include "channels.h"
 #include "servers-setup.h"
+#include "recode.h"
 
 #include "autorun.h"
 #include "fe-core-commands.h"
@@ -46,6 +50,7 @@
 #include "window-activity.h"
 #include "window-items.h"
 #include "windows-layout.h"
+#include "fe-recode.h"
 
 #include <signal.h>
 
@@ -92,6 +97,8 @@ void fe_settings_deinit(void);
 void window_commands_init(void);
 void window_commands_deinit(void);
 
+static void sig_setup_changed(void);
+
 static void print_version(void)
 {
        printf(PACKAGE" " IRSSI_VERSION" (%d %04d)\n",
@@ -106,7 +113,8 @@ static void sig_connected(SERVER_REC *server)
 
 static void sig_disconnected(SERVER_REC *server)
 {
-       g_free(MODULE_DATA(server));
+       void *data = MODULE_DATA(server);
+       g_free(data);
        MODULE_DATA_UNSET(server);
 }
 
@@ -117,7 +125,9 @@ static void sig_channel_created(CHANNEL_REC *channel)
 
 static void sig_channel_destroyed(CHANNEL_REC *channel)
 {
-       g_free(MODULE_DATA(channel));
+       void *data = MODULE_DATA(channel);
+
+       g_free(data);
        MODULE_DATA_UNSET(channel);
 }
 
@@ -132,7 +142,7 @@ void fe_common_core_init(void)
        static struct poptOption options[] = {
                { NULL, '\0', POPT_ARG_INCLUDE_TABLE, version_options, 0, NULL, NULL },
                POPT_AUTOHELP
-               { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server", "SERVER" },
+               { "connect", 'c', POPT_ARG_STRING, &autocon_server, 0, "Automatically connect to server/network", "SERVER" },
                { "password", 'w', POPT_ARG_STRING, &autocon_password, 0, "Autoconnect password", "PASSWORD" },
                { "port", 'p', POPT_ARG_INT, &autocon_port, 0, "Autoconnect port", "PORT" },
                { "noconnect", '!', POPT_ARG_NONE, &no_autoconnect, 0, "Disable autoconnecting", NULL },
@@ -150,11 +160,11 @@ void fe_common_core_init(void)
        args_register(options);
 
        settings_add_bool("lookandfeel", "timestamps", TRUE);
-       settings_add_str("lookandfeel", "timestamp_level", "ALL");
-       settings_add_int("lookandfeel", "timestamp_timeout", 0);
+       settings_add_level("lookandfeel", "timestamp_level", "ALL");
+       settings_add_time("lookandfeel", "timestamp_timeout", "0");
 
        settings_add_bool("lookandfeel", "bell_beeps", FALSE);
-       settings_add_str("lookandfeel", "beep_msg_level", "");
+       settings_add_level("lookandfeel", "beep_msg_level", "");
        settings_add_bool("lookandfeel", "beep_when_window_active", TRUE);
        settings_add_bool("lookandfeel", "beep_when_away", TRUE);
 
@@ -164,7 +174,13 @@ void fe_common_core_init(void)
 
        settings_add_bool("lookandfeel", "use_status_window", TRUE);
        settings_add_bool("lookandfeel", "use_msgs_window", FALSE);
-
+#if defined (HAVE_NL_LANGINFO) && defined(CODESET)
+       settings_add_str("lookandfeel", "term_charset", 
+                        *nl_langinfo(CODESET) != '\0' ? 
+                        nl_langinfo(CODESET) : "ISO8859-1");
+#else
+       settings_add_str("lookandfeel", "term_charset", "ISO8859-1");
+#endif
        themes_init();
         theme_register(fecommon_core_formats);
 
@@ -197,6 +213,7 @@ void fe_common_core_init(void)
        fe_messages_init();
        hilight_text_init();
        fe_ignore_messages_init();
+       fe_recode_init();
 
        settings_check();
 
@@ -239,10 +256,12 @@ void fe_common_core_deinit(void)
 
        fe_messages_deinit();
        fe_ignore_messages_deinit();
+       fe_recode_deinit();
 
         theme_unregister();
        themes_deinit();
 
+        signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed);
         signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
         signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
         signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
@@ -279,25 +298,42 @@ void glog_func(const char *log_domain, GLogLevelFlags log_level,
 static void create_windows(void)
 {
        WINDOW_REC *window;
-
-       windows_layout_restore();
-       if (windows != NULL)
-               return;
-
-       if (settings_get_bool("use_status_window")) {
-               window = window_create(NULL, TRUE);
-               window_set_name(window, "(status)");
-               window_set_level(window, MSGLEVEL_ALL ^
-                                (settings_get_bool("use_msgs_window") ?
-                                 MSGS_WINDOW_LEVELS : 0));
-                window_set_immortal(window, TRUE);
+       int have_status = settings_get_bool("use_status_window");
+
+       window = window_find_name("(status)");
+       if (have_status) {
+               if (window == NULL) {
+                       window = window_create(NULL, TRUE);
+                       window_set_refnum(window, 1);
+                       window_set_name(window, "(status)");
+                       window_set_level(window, MSGLEVEL_ALL ^
+                                        (settings_get_bool("use_msgs_window") ?
+                                         MSGS_WINDOW_LEVELS : 0));
+                       window_set_immortal(window, TRUE);
+               }
+       } else {
+               if (window != NULL) {
+                       window_set_name(window, NULL);
+                       window_set_level(window, 0);
+                       window_set_immortal(window, FALSE);
+               }
        }
 
+       window = window_find_name("(msgs)");
        if (settings_get_bool("use_msgs_window")) {
-               window = window_create(NULL, TRUE);
-               window_set_name(window, "(msgs)");
-               window_set_level(window, MSGS_WINDOW_LEVELS);
-                window_set_immortal(window, TRUE);
+               if (window == NULL) {
+                       window = window_create(NULL, TRUE);
+                       window_set_refnum(window, have_status ? 2 : 1);
+                       window_set_name(window, "(msgs)");
+                       window_set_level(window, MSGS_WINDOW_LEVELS);
+                       window_set_immortal(window, TRUE);
+               }
+       } else {
+               if (window != NULL) {
+                       window_set_name(window, NULL);
+                       window_set_level(window, 0);
+                       window_set_immortal(window, FALSE);
+               }
        }
 
        if (windows == NULL) {
@@ -345,6 +381,34 @@ static void autoconnect_servers(void)
        g_slist_free(chatnets);
 }
 
+static void sig_setup_changed(void)
+{
+       static int firsttime = TRUE;
+       static int status_window = FALSE, msgs_window = FALSE;
+       int changed = FALSE;
+
+       if (settings_get_bool("use_status_window") != status_window) {
+               status_window = !status_window;
+               changed = TRUE;
+       }
+       if (settings_get_bool("use_msgs_window") != msgs_window) {
+               msgs_window = !msgs_window;
+               changed = TRUE;
+       }
+
+       if (firsttime) {
+               firsttime = FALSE;
+               changed = TRUE;
+
+               windows_layout_restore();
+               if (windows != NULL)
+                       return;
+       }
+
+       if (changed)
+               create_windows();
+}
+
 void fe_common_core_finish_init(void)
 {
        int setup_changed;
@@ -356,7 +420,7 @@ void fe_common_core_finish_init(void)
 #endif
 
         setup_changed = FALSE;
-       if (cmdline_nick != NULL) {
+       if (cmdline_nick != NULL && *cmdline_nick != '\0') {
                /* override nick found from setup */
                settings_set_str("nick", cmdline_nick);
                setup_changed = TRUE;
@@ -368,13 +432,22 @@ void fe_common_core_finish_init(void)
                setup_changed = TRUE;
        }
 
-       create_windows();
+       sig_setup_changed();
+       signal_add_first("setup changed", (SIGNAL_FUNC) sig_setup_changed);
 
         /* _after_ windows are created.. */
+#if GLIB_CHECK_VERSION(2,6,0)
+       g_log_set_default_handler((GLogFunc) glog_func, NULL);
+#else
        g_log_set_handler(G_LOG_DOMAIN,
                          (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
                                            G_LOG_LEVEL_WARNING),
                          (GLogFunc) glog_func, NULL);
+       g_log_set_handler("GLib",
+                         (GLogLevelFlags) (G_LOG_LEVEL_CRITICAL |
+                                           G_LOG_LEVEL_WARNING),
+                         (GLogFunc) glog_func, NULL); /* send glib errors to the same place */
+#endif
 
        if (setup_changed)
                 signal_emit("setup changed", 0);
index b599c8109cbbb79d2c25367f41bd8375957356a5..69bab326283165f03ade7d00c87dcbccd8cec981 100644 (file)
@@ -18,6 +18,7 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#include "core.h"
 #include "module.h"
 #include "module-formats.h"
 #include "signals.h"
@@ -47,7 +48,11 @@ static int ret_texts[] = {
        TXT_CHAN_NOT_FOUND,
        TXT_CHAN_NOT_SYNCED,
         TXT_ILLEGAL_PROTO,
-       TXT_NOT_GOOD_IDEA
+       TXT_NOT_GOOD_IDEA,
+        TXT_INVALID_TIME,
+        TXT_INVALID_CHARSET,
+        TXT_EVAL_MAX_RECURSE,
+        TXT_PROGRAM_NOT_FOUND
 };
 
 int command_hide_output;
@@ -190,6 +195,22 @@ static void cmd_join(const char *data, SERVER_REC *server)
        cmd_params_free(free_arg);
 }
 
+/* SYNTAX: UPTIME */
+static void cmd_uptime(char *data)
+{
+       time_t uptime;
+
+       g_return_if_fail(data != NULL);
+
+       if (*data == '\0') {
+               uptime = time(NULL) - client_start_time;
+               printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                         "Uptime: %ldd %ldh %ldm %lds",
+                         uptime/3600/24, uptime/3600%24,
+                         uptime/60%60, uptime%60);
+       }
+}
+
 static void sig_stop(void)
 {
        signal_stop();
@@ -214,8 +235,8 @@ static void event_command(const char *data)
        cmdchar = *data == '\0' ? NULL :
                strchr(settings_get_str("cmdchars"), *data);
        if (cmdchar != NULL && (data[1] == '^' ||
-                               (data[1] == *cmdchar && data[2] == '^'))) {
-                command_hide_output = TRUE;
+                               (data[1] == *cmdchar && data[2] == '^'))
+                           && !command_hide_output++) {
                signal_add_first("print starting", (SIGNAL_FUNC) sig_stop);
                signal_add_first("print format", (SIGNAL_FUNC) sig_stop);
                signal_add_first("print text", (SIGNAL_FUNC) sig_stop);
@@ -224,8 +245,7 @@ static void event_command(const char *data)
 
 static void event_command_last(const char *data)
 {
-       if (command_hide_output) {
-               command_hide_output = FALSE;
+       if (command_hide_output && !--command_hide_output) {
                signal_remove("print starting", (SIGNAL_FUNC) sig_stop);
                signal_remove("print format", (SIGNAL_FUNC) sig_stop);
                signal_remove("print text", (SIGNAL_FUNC) sig_stop);
@@ -315,7 +335,7 @@ static void event_list_subcommands(const char *command)
 
 void fe_core_commands_init(void)
 {
-       command_hide_output = FALSE;
+       command_hide_output = 0;
 
        command_cmd = FALSE;
        memset(&time_command_now, 0, sizeof(GTimeVal));
@@ -324,6 +344,7 @@ void fe_core_commands_init(void)
        command_bind("version", NULL, (SIGNAL_FUNC) cmd_version);
        command_bind("cat", NULL, (SIGNAL_FUNC) cmd_cat);
        command_bind("beep", NULL, (SIGNAL_FUNC) cmd_beep);
+       command_bind("uptime", NULL, (SIGNAL_FUNC) cmd_uptime);
        command_bind_first("nick", NULL, (SIGNAL_FUNC) cmd_nick);
        command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_join);
 
@@ -342,6 +363,7 @@ void fe_core_commands_deinit(void)
        command_unbind("version", (SIGNAL_FUNC) cmd_version);
        command_unbind("cat", (SIGNAL_FUNC) cmd_cat);
        command_unbind("beep", (SIGNAL_FUNC) cmd_beep);
+       command_unbind("uptime", (SIGNAL_FUNC) cmd_uptime);
        command_unbind("nick", (SIGNAL_FUNC) cmd_nick);
        command_unbind("join", (SIGNAL_FUNC) cmd_join);
 
index 68ff1c437ac6ebbad8b52e22dd1a81f14ce7f6bc..3fa8899551c4b745562bd975ea34bfca5d6dde58 100644 (file)
@@ -28,6 +28,7 @@
 #include "misc.h"
 #include "levels.h"
 
+#include "servers.h"
 #include "channels.h"
 #include "queries.h"
 
@@ -187,6 +188,7 @@ static void process_destroy(PROCESS_REC *rec, int status)
 
        g_free_not_null(rec->name);
        g_free_not_null(rec->target);
+       g_free_not_null(rec->target_server);
         g_free(rec->args);
         g_free(rec);
 }
@@ -347,7 +349,7 @@ static void process_exec(PROCESS_REC *rec, const char *cmd)
 static void sig_exec_input_reader(PROCESS_REC *rec)
 {
         char tmpbuf[512], *str;
-        unsigned int recvlen;
+        gsize recvlen;
        int err, ret;
 
        g_return_if_fail(rec != NULL);
@@ -375,9 +377,10 @@ static void sig_exec_input_reader(PROCESS_REC *rec)
 }
 
 static void handle_exec(const char *args, GHashTable *optlist,
-                       WI_ITEM_REC *item)
+                        SERVER_REC *server, WI_ITEM_REC *item)
 {
        PROCESS_REC *rec;
+       SERVER_REC *target_server;
         char *target, *level;
        int notice, signum, interactive, target_nick, target_channel;
 
@@ -394,6 +397,7 @@ static void handle_exec(const char *args, GHashTable *optlist,
        }
 
        target = NULL;
+       target_server = NULL;
        notice = FALSE;
 
        if (g_hash_table_lookup(optlist, "in") != NULL) {
@@ -418,13 +422,16 @@ static void handle_exec(const char *args, GHashTable *optlist,
                if (item == NULL)
                        cmd_return_error(CMDERR_NOT_JOINED);
                target = (char *) window_item_get_target(item);
+               target_server = item->server;
                target_channel = IS_CHANNEL(item);
                target_nick = IS_QUERY(item);
        } else if (g_hash_table_lookup(optlist, "msg") != NULL) {
                 /* redirect output to /msg <nick> */
                target = g_hash_table_lookup(optlist, "msg");
+               target_server = server;
        } else if (g_hash_table_lookup(optlist, "notice") != NULL) {
                target = g_hash_table_lookup(optlist, "notice");
+               target_server = server;
                 notice = TRUE;
        }
 
@@ -456,6 +463,8 @@ static void handle_exec(const char *args, GHashTable *optlist,
                         /* redirect output to target */
                        g_free_and_null(rec->target);
                        rec->target = g_strdup(target);
+                       rec->target_server = target_server == NULL ? NULL :
+                               g_strdup(target_server->tag);
                         rec->notice = notice;
                }
 
@@ -496,6 +505,8 @@ static void handle_exec(const char *args, GHashTable *optlist,
 
         rec->id = process_get_new_id();
        rec->target = g_strdup(target);
+       rec->target_server = target_server == NULL ? NULL :
+               g_strdup(target_server->tag);
        rec->target_win = active_win;
        rec->target_channel = target_channel;
        rec->target_nick = target_nick;
@@ -535,7 +546,7 @@ static void cmd_exec(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
        if (cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
                           PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
                           "exec", &optlist, &args)) {
-               handle_exec(args, optlist, item);
+               handle_exec(args, optlist, server, item);
                cmd_params_free(free_arg);
        }
 }
@@ -584,9 +595,18 @@ static void sig_exec_input(PROCESS_REC *rec, const char *text)
        server = NULL;
 
        if (rec->target != NULL) {
-               item = window_item_find(NULL, rec->target);
-               server = item != NULL ? item->server :
-                       active_win->active_server;
+               if (rec->target_server != NULL) {
+                       server = server_find_tag(rec->target_server);
+                       if (server == NULL) {
+                               /* disconnected - target is lost */
+                               return;
+                       }
+                       item = NULL;
+               } else {
+                       item = window_item_find(NULL, rec->target);
+                       server = item != NULL ? item->server :
+                               active_win->active_server;
+               }
 
                str = g_strconcat(rec->target_nick ? "-nick " :
                                  rec->target_channel ? "-channel " : "",
index 606b230211fd2db26a574abb416f8526d3cfc8b9..20219d5adf94f59b6b296bea745c94958582865a 100644 (file)
@@ -32,6 +32,7 @@ struct PROCESS_REC {
 
         int level; /* what level to use when printing the text */
         char *target; /* send text with /msg <target> ... */
+       char *target_server;
        WINDOW_REC *target_win; /* print text to this window */
         EXEC_WI_REC *target_item; /* print text to this exec window item */
 
index 84a7635222197d61ee7cb2269ab57193c156188a..c093b84b296547e249e4258f30d0c04a21231c9e 100644 (file)
@@ -258,6 +258,7 @@ static void cmd_help(const char *data)
        ptr = cmd+strlen(cmd);
        while (ptr[-1] == ' ') ptr--; *ptr = '\0';
 
+       g_strdown(cmd);
        show_help(cmd);
         g_free(cmd);
 }
index 4621ab2628ece9ede9a6a2e85e560434238eb403..d2f48f20d0e520e6dcc931bd58f2246cd02dec16 100644 (file)
@@ -63,6 +63,8 @@ static void ignore_print(int index, IGNORE_REC *rec)
        }
        if (rec->fullword) g_string_append(options, "-full ");
        if (rec->replies) g_string_append(options, "-replies ");
+       if (rec->servertag != NULL) 
+               g_string_sprintfa(options, "-network %s ", rec->servertag);
        if (rec->pattern != NULL)
                g_string_sprintfa(options, "-pattern %s ", rec->pattern);
 
@@ -105,17 +107,18 @@ static void cmd_ignore_show(void)
 }
 
 /* SYNTAX: IGNORE [-regexp | -full] [-pattern <pattern>] [-except] [-replies]
-                  [-channels <channel>] [-time <secs>] <mask> [<levels>]
+                  [-network <network>] [-channels <channel>] [-time <secs>] <mask> [<levels>]
            IGNORE [-regexp | -full] [-pattern <pattern>] [-except] [-replies]
-                 [-time <secs>] <channels> [<levels>] */
+                 [-network <network>] [-time <secs>] <channels> [<levels>] */
+/* NOTE: -network replaces the old -ircnet flag. */
 static void cmd_ignore(const char *data)
 {
        GHashTable *optlist;
        IGNORE_REC *rec;
-       char *patternarg, *chanarg, *mask, *levels, *timestr;
+       char *patternarg, *chanarg, *mask, *levels, *timestr, *servertag;
        char **channels;
        void *free_arg;
-       int new_ignore;
+       int new_ignore, msecs;
 
        if (*data == '\0') {
                cmd_ignore_show();
@@ -128,10 +131,21 @@ static void cmd_ignore(const char *data)
 
        patternarg = g_hash_table_lookup(optlist, "pattern");
         chanarg = g_hash_table_lookup(optlist, "channels");
-
+       servertag = g_hash_table_lookup(optlist, "network");
+       /* Allow -ircnet for backwards compatibility */
+       if (!servertag)
+               servertag = g_hash_table_lookup(optlist, "ircnet");
+       
        if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
         if (*levels == '\0') levels = "ALL";
 
+       msecs = 0;
+       timestr = g_hash_table_lookup(optlist, "time");
+       if (timestr != NULL) {
+               if (!parse_time_interval(timestr, &msecs))
+                       cmd_param_error(CMDERR_INVALID_TIME);
+       }
+
        if (active_win->active_server != NULL &&
            server_ischannel(active_win->active_server, mask)) {
                chanarg = mask;
@@ -166,16 +180,16 @@ static void cmd_ignore(const char *data)
                cmd_params_free(free_arg);
                 return;
        }
-
+       rec->servertag = (servertag == NULL || *servertag == '\0') ?
+               NULL : g_strdup(servertag);
        rec->pattern = (patternarg == NULL || *patternarg == '\0') ?
                NULL : g_strdup(patternarg);
        rec->exception = g_hash_table_lookup(optlist, "except") != NULL;
        rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
        rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
        rec->replies = g_hash_table_lookup(optlist, "replies") != NULL;
-       timestr = g_hash_table_lookup(optlist, "time");
-        if (timestr != NULL)
-               rec->unignore_time = time(NULL)+atoi(timestr);
+       if (msecs != 0)
+               rec->unignore_time = time(NULL)+msecs/1000;
 
        if (new_ignore)
                ignore_add_rec(rec);
@@ -197,7 +211,7 @@ static void cmd_unignore(const char *data)
                return;
 
        if (*mask == '\0')
-                cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
+                cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
 
        if (is_numeric(mask, ' ')) {
                /* with index number */
@@ -248,7 +262,7 @@ void fe_ignore_init(void)
        signal_add("ignore created", (SIGNAL_FUNC) sig_ignore_created);
        signal_add("ignore changed", (SIGNAL_FUNC) sig_ignore_created);
 
-       command_set_options("ignore", "regexp full except replies -time -pattern -channels");
+       command_set_options("ignore", "regexp full except replies -network -ircnet -time -pattern -channels");
 }
 
 void fe_ignore_deinit(void)
index 1db74fe11a0d2b73722960c456d8fdaf1c34a83d..5b2a7780c59065f34d58f6a3597b2bb6cb02c0a1 100644 (file)
@@ -109,6 +109,8 @@ static void cmd_log_open(const char *data)
                targetarg = g_hash_table_lookup(optlist, "targets");
                if (targetarg != NULL && *targetarg != '\0')
                        log_add_targets(log, targetarg, servertag);
+               else if (servertag != NULL)
+                       log_add_targets(log, "*", servertag);
        }
 
        if (g_hash_table_lookup(optlist, "autoopen"))
@@ -189,17 +191,20 @@ static char *log_items_get_list(LOG_REC *log)
        GSList *tmp;
        GString *str;
        char *ret;
+       LOG_ITEM_REC *rec = NULL;
 
        g_return_val_if_fail(log != NULL, NULL);
        g_return_val_if_fail(log->items != NULL, NULL);
 
        str = g_string_new(NULL);
        for (tmp = log->items; tmp != NULL; tmp = tmp->next) {
-               LOG_ITEM_REC *rec = tmp->data;
+               rec = tmp->data;
 
                 g_string_sprintfa(str, "%s, ", rec->name);
        }
        g_string_truncate(str, str->len-2);
+       if(rec->servertag != NULL)
+               g_string_sprintfa(str, " (%s)", rec->servertag);
 
        ret = str->str;
        g_string_free(str, FALSE);
@@ -671,7 +676,7 @@ static void read_settings(void)
 
        autolog_path = settings_get_str("autolog_path");
        autolog_level = !settings_get_bool("autolog") ? 0 :
-               level2bits(settings_get_str("autolog_level"));
+               settings_get_level("autolog_level");
 
        if (old_autolog && !autolog_level)
                autologs_close_all();
@@ -704,7 +709,7 @@ void fe_log_init(void)
         settings_add_bool("log", "autolog", FALSE);
        settings_add_bool("log", "autolog_colors", FALSE);
         settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log");
-       settings_add_str("log", "autolog_level", "all -crap -clientcrap -ctcps");
+       settings_add_level("log", "autolog_level", "all -crap -clientcrap -ctcps");
         settings_add_str("log", "log_theme", "");
 
        autolog_level = 0;
index 31bbbfad056125c3ccee7b1dd7844af09ecbd423..3080b99caa16b4ffb4b9e2f28e51990028e106b1 100644 (file)
@@ -31,6 +31,7 @@
 #include "channels.h"
 #include "nicklist.h"
 #include "ignore.h"
+#include "recode.h"
 
 #include "window-items.h"
 #include "fe-queries.h"
 #include "printtext.h"
 
 #define ishighalnum(c) ((unsigned char) (c) >= 128 || i_isalnum(c))
+#define isnickchar(a) \
+       (i_isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
+       (a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
+       (a) == '|' || (a) == '\\' || (a) == '^')
 
 GHashTable *printnicks;
 
@@ -67,7 +72,7 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
 
                /* check that the beginning marker starts a word, and
                   that the matching end marker ends a word */
-               if ((pos > 0 && !i_isspace(bgn[-1])) || !ishighalnum(bgn[1]))
+               if ((pos > 0 && bgn[-1] != ' ') || !ishighalnum(bgn[1]))
                        continue;
                if ((end = strchr(bgn+1, *bgn)) == NULL)
                        continue;
@@ -80,12 +85,25 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
                           use emphasis on them. */
                        int found;
                         char c;
+                       char *end2; 
 
+                       /* check if _foo_ is a nick */
                        c = end[1];
                         end[1] = '\0';
                         found = nicklist_find(CHANNEL(item), bgn) != NULL;
                        end[1] = c;
                        if (found) continue;
+                       
+                       /* check if the whole 'word' (e.g. "_foo_^") is a nick
+                          in "_foo_^ ", end will be the second _, end2 the ^ */
+                       end2 = end;
+                       while (isnickchar(end2[1]))
+                               end2++;
+                       c = end2[1];
+                       end2[1] = '\0';
+                       found = nicklist_find(CHANNEL(item), bgn) != NULL;
+                       end2[1] = c;
+                       if (found) continue;
                }
 
                /* allow only *word* emphasis, not *multiple words* */
@@ -116,12 +134,19 @@ char *expand_emphasis(WI_ITEM_REC *item, const char *text)
 static char *channel_get_nickmode_rec(NICK_REC *nickrec)
 {
         char *emptystr;
+       static char nickmode[2]; /* FIXME: bad */
 
        if (!settings_get_bool("show_nickmode"))
                 return "";
 
         emptystr = settings_get_bool("show_nickmode_empty") ? " " : "";
 
+       if (nickrec != NULL && nickrec->other) {
+               nickmode[0] = nickrec->other;
+               nickmode[1] = '\0';
+               return nickmode;
+       }
+
        return nickrec == NULL ? emptystr :
                nickrec->op ? "@" :
                nickrec->halfop ? "%" :
@@ -145,6 +170,7 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
        const char *nickmode, *printnick;
        int for_me, print_channel, level;
        char *color, *freemsg = NULL;
+       HILIGHT_REC *hilight;
 
        /* NOTE: this may return NULL if some channel is just closed with
           /WINDOW CLOSE and server still sends the few last messages */
@@ -154,8 +180,9 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
 
        for_me = !settings_get_bool("hilight_nick_matches") ? FALSE :
                nick_match_msg(chanrec, msg, server->nick);
-       color = for_me ? NULL :
+       hilight = for_me ? NULL :
                hilight_match_nick(server, target, nick, address, MSGLEVEL_PUBLIC, msg);
+       color = (hilight == NULL) ? NULL : hilight_get_color(hilight);
 
        print_channel = chanrec == NULL ||
                !window_item_is_active((WI_ITEM_REC *) chanrec);
@@ -164,7 +191,7 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
                print_channel = TRUE;
 
        level = MSGLEVEL_PUBLIC;
-       if (for_me || color != NULL)
+       if (for_me)
                level |= MSGLEVEL_HILIGHT;
 
        if (settings_get_bool("emphasis"))
@@ -178,34 +205,31 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
        if (printnick == NULL)
                printnick = nick;
 
-       if (!print_channel) {
-               /* message to active channel in window */
-               if (color != NULL) {
-                       /* highlighted nick */
-                       printformat(server, target, level,
-                                   TXT_PUBMSG_HILIGHT,
-                                   color, printnick, msg, nickmode);
-               } else {
+       if (color != NULL) {
+               /* highlighted nick */
+               TEXT_DEST_REC dest;
+               format_create_dest(&dest, server, target, level, NULL);
+               hilight_update_text_dest(&dest,hilight);
+               if (!print_channel) /* message to active channel in window */
+                       printformat_dest(&dest, TXT_PUBMSG_HILIGHT, color,
+                                        printnick, msg, nickmode);
+               else /* message to not existing/active channel */
+                       printformat_dest(&dest, TXT_PUBMSG_HILIGHT_CHANNEL,
+                                        color, printnick, target, msg,
+                                        nickmode);
+       } else {
+               if (!print_channel)
                        printformat(server, target, level,
                                    for_me ? TXT_PUBMSG_ME : TXT_PUBMSG,
                                    printnick, msg, nickmode);
-               }
-       } else {
-               /* message to not existing/active channel */
-               if (color != NULL) {
-                       /* highlighted nick */
-                       printformat(server, target, level,
-                                   TXT_PUBMSG_HILIGHT_CHANNEL,
-                                   color, printnick, target, msg, nickmode);
-               } else {
+               else
                        printformat(server, target, level,
                                    for_me ? TXT_PUBMSG_ME_CHANNEL :
                                    TXT_PUBMSG_CHANNEL,
                                    printnick, target, msg, nickmode);
-               }
-       }
+       }                                               
 
-        g_free_not_null(freemsg);
+       g_free_not_null(freemsg);
        g_free_not_null(color);
 }
 
@@ -233,9 +257,8 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
        WINDOW_REC *window;
        CHANNEL_REC *channel;
        const char *nickmode;
-        char *freemsg = NULL;
+        char *freemsg = NULL, *recoded;
        int print_channel;
-
        channel = channel_find(server, target);
        if (channel != NULL)
                target = channel->visible_name;
@@ -255,14 +278,18 @@ static void sig_message_own_public(SERVER_REC *server, const char *msg,
        if (settings_get_bool("emphasis"))
                msg = freemsg = expand_emphasis((WI_ITEM_REC *) channel, msg);
 
+       /* ugly: recode the sent message back for printing */ 
+       recoded = recode_in(server, msg, target);
+       
        if (!print_channel) {
                printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
-                           TXT_OWN_MSG, server->nick, msg, nickmode);
+                           TXT_OWN_MSG, server->nick, recoded, nickmode);
        } else {
                printformat(server, target, MSGLEVEL_PUBLIC | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
-                           TXT_OWN_MSG_CHANNEL, server->nick, target, msg, nickmode);
+                           TXT_OWN_MSG_CHANNEL, server->nick, target, recoded, nickmode);
        }
 
+       g_free(recoded);
        g_free_not_null(freemsg);
 }
 
@@ -270,11 +297,10 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
                                    const char *target, const char *origtarget)
 {
        QUERY_REC *query;
-        char *freemsg = NULL;
+        char *freemsg = NULL, *recoded;
 
        g_return_if_fail(server != NULL);
        g_return_if_fail(msg != NULL);
-
        if (target == NULL) {
                /* this should only happen if some special target failed and
                   we should display some error message. currently the special
@@ -294,11 +320,15 @@ static void sig_message_own_private(SERVER_REC *server, const char *msg,
        if (settings_get_bool("emphasis"))
                msg = freemsg = expand_emphasis((WI_ITEM_REC *) query, msg);
 
+       /* ugly: recode the sent message back for printing */
+       recoded = recode_in(server, msg, target);
+
        printformat(server, target,
                    MSGLEVEL_MSGS | MSGLEVEL_NOHILIGHT | MSGLEVEL_NO_ACT,
                    query == NULL ? TXT_OWN_MSG_PRIVATE :
-                   TXT_OWN_MSG_PRIVATE_QUERY, target, msg, server->nick);
+                   TXT_OWN_MSG_PRIVATE_QUERY, target, recoded, server->nick);
 
+       g_free(recoded);
        g_free_not_null(freemsg);
 }
 
@@ -323,7 +353,7 @@ static void sig_message_quit(SERVER_REC *server, const char *nick,
        WINDOW_REC *window;
        GString *chans;
        GSList *tmp, *windows;
-       char *print_channel;
+       char *print_channel, *recoded;
        int once, count;
 
        if (ignore_check(server, nick, address, NULL, reason, MSGLEVEL_QUITS))
@@ -356,10 +386,12 @@ static void sig_message_quit(SERVER_REC *server, const char *nick,
                        window = window_item_window((WI_ITEM_REC *) rec);
                        if (g_slist_find(windows, window) == NULL) {
                                windows = g_slist_append(windows, window);
+                               recoded = recode_in(server, reason, rec->visible_name);
                                printformat(server, rec->visible_name,
                                            MSGLEVEL_QUITS,
-                                           TXT_QUIT, nick, address, reason,
+                                           TXT_QUIT, nick, address, recoded,
                                            rec->visible_name);
+                               g_free(recoded);
                        }
                }
                count++;
@@ -371,17 +403,22 @@ static void sig_message_quit(SERVER_REC *server, const char *nick,
                   display the quit there too */
                QUERY_REC *query = query_find(server, nick);
                if (query != NULL) {
+                       recoded = recode_in(server, reason, nick);
                        printformat(server, nick, MSGLEVEL_QUITS,
-                                   TXT_QUIT, nick, address, reason, "");
+                                   TXT_QUIT, nick, address, recoded, "");
+                       g_free(recoded);
                }
        }
 
        if (once || count == 0) {
                if (chans->len > 0)
                        g_string_truncate(chans, chans->len-1);
+               /* at least recode_fallback will be used */
+               recoded = recode_in(server, reason, nick);
                printformat(server, print_channel, MSGLEVEL_QUITS,
                            count <= 1 ? TXT_QUIT : TXT_QUIT_ONCE,
-                           nick, address, reason, chans->str);
+                           nick, address, recoded, chans->str);
+               g_free(recoded);
        }
        g_string_free(chans, TRUE);
 }
index 5bbcf0b53ab61a5e54898a6ca845e8db1eb3cc28..3c3c979022885f07a2aa4d1cb302251cff30a600 100644 (file)
@@ -130,7 +130,7 @@ static char **module_prefixes_get(void)
         char **list, *name;
         int count;
 
-       list = g_new(char *, 2 + 2*g_slist_length(chat_protocols));
+       list = g_new(char *, 2 + 3*g_slist_length(chat_protocols));
        list[0] = "fe";
 
        count = 1;
@@ -142,6 +142,7 @@ static char **module_prefixes_get(void)
 
                list[count++] = name;
                 list[count++] = g_strconcat("fe_", name, NULL);
+                list[count++] = g_strconcat("fe_common_", name, NULL);
        }
        list[count] = NULL;
 
index 8345d52f06b79adcd8f4bd7edd158e2249e83b5a..f1763814103ea11576770e23172623ab19df8872 100644 (file)
@@ -322,7 +322,7 @@ static int sig_query_autoclose(void)
                    now-rec->last_unread_msg > query_auto_close)
                        query_destroy(rec);
        }
-        return 1;
+     return 1;
 }
 
 static void sig_message_private(SERVER_REC *server, const char *msg,
@@ -340,8 +340,8 @@ static void sig_message_private(SERVER_REC *server, const char *msg,
 
 static void read_settings(void)
 {
-       querycreate_level = level2bits(settings_get_str("autocreate_query_level"));
-       query_auto_close = settings_get_int("autoclose_query");
+       querycreate_level = settings_get_level("autocreate_query_level");
+       query_auto_close = settings_get_time("autoclose_query")/1000;
        if (query_auto_close > 0 && queryclose_tag == -1)
                queryclose_tag = g_timeout_add(5000, (GSourceFunc) sig_query_autoclose, NULL);
        else if (query_auto_close <= 0 && queryclose_tag != -1) {
@@ -352,9 +352,9 @@ static void read_settings(void)
 
 void fe_queries_init(void)
 {
-       settings_add_str("lookandfeel", "autocreate_query_level", "MSGS DCCMSGS");
+       settings_add_level("lookandfeel", "autocreate_query_level", "MSGS DCCMSGS");
        settings_add_bool("lookandfeel", "autocreate_own_query", TRUE);
-       settings_add_int("lookandfeel", "autoclose_query", 0);
+       settings_add_time("lookandfeel", "autoclose_query", "0");
 
        queryclose_tag = -1;
        read_settings();
diff --git a/apps/irssi/src/fe-common/core/fe-recode.c b/apps/irssi/src/fe-common/core/fe-recode.c
new file mode 100644 (file)
index 0000000..988c376
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ fe-recode.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 "modules.h"
+#include "module-formats.h"
+#include "commands.h"
+#include "levels.h"
+#include "lib-config/iconfig.h"
+#include "settings.h"
+#include "printtext.h"
+#include "formats.h"
+#include "recode.h"
+
+#ifdef HAVE_NL_LANGINFO
+#  include <langinfo.h>
+#endif
+
+#define SLIST_FOREACH(var, head)               \
+for ((var) = (head);                           \
+                (var);                                 \
+                (var) = g_slist_next((var)))
+
+#ifdef HAVE_GLIB2
+char *recode_fallback = NULL;
+char *recode_out_default = NULL;
+char *term_charset = NULL;
+
+static const char *fe_recode_get_target (WI_ITEM_REC *witem)
+{
+       if (witem && (witem->type == module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY")
+           || witem->type == module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL")))
+               return window_item_get_target(witem);
+
+       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_CHANNEL_OR_QUERY);
+       return NULL;
+}
+
+static int fe_recode_compare_func (CONFIG_NODE *node1, CONFIG_NODE *node2)
+{
+       return strcmp(node1->key, node2->key);
+}
+
+/* SYNTAX: RECODE */
+static void fe_recode_cmd (const char *data, SERVER_REC *server, WI_ITEM_REC *witem)
+{
+       if (*data)
+               command_runsub("recode", data, server, witem);
+       else {
+               CONFIG_NODE *conversions;
+               GSList *tmp;
+               GSList *sorted = NULL;
+
+               conversions = iconfig_node_traverse("conversions", FALSE);
+
+               for (tmp = conversions ? config_node_first(conversions->value) : NULL;
+                    tmp != NULL;
+                    tmp = config_node_next(tmp)) {
+                       CONFIG_NODE *node = tmp->data;
+
+                       if (node->type == NODE_TYPE_KEY)
+                               sorted = g_slist_insert_sorted(sorted, node, (GCompareFunc) fe_recode_compare_func);
+               }
+
+               printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_RECODE_HEADER);
+               SLIST_FOREACH(tmp, sorted)
+               {
+                       CONFIG_NODE *node = tmp->data;
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_RECODE_LINE, node->key, node->value);
+               }
+
+               g_slist_free(sorted);
+       }
+}
+
+/* SYNTAX: RECODE ADD [[<tag>/]<target>] <charset> */
+static void fe_recode_add_cmd (const char *data, SERVER_REC *server, WI_ITEM_REC *witem)
+{
+       const char *first;
+       const char *second;
+       const char *target;
+       const char *charset;
+       void *free_arg;
+
+       if (! cmd_get_params(data, &free_arg, 2, &first, &second))
+               return;
+
+       if (! *first)
+               cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
+
+       if (*second) {
+               target = first;
+               charset = second;
+       } else {
+               target = fe_recode_get_target(witem);
+               charset = first;
+               if (! target)
+                       goto end;
+       }
+       if (is_valid_charset(charset)) {
+               iconfig_set_str("conversions", target, charset);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONVERSION_ADDED, target, charset);
+       } else
+               signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_INVALID_CHARSET), charset);
+ end:
+       cmd_params_free(free_arg);
+}
+
+/* SYNTAX: RECODE REMOVE [<target>] */
+static void fe_recode_remove_cmd (const char *data, SERVER_REC *server, WI_ITEM_REC *witem)
+{
+       const char *target;
+       void *free_arg;
+
+       if (! cmd_get_params(data, &free_arg, 1, &target))
+               return;
+
+       if (! *target) {
+               target = fe_recode_get_target(witem);
+               if (! target)
+                       goto end;
+       }
+
+       if (iconfig_get_str("conversions", target, NULL) == NULL)
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONVERSION_NOT_FOUND, target);
+       else    {
+               iconfig_set_str("conversions", target, NULL);
+               printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONVERSION_REMOVED, target);
+       }
+
+ end:
+       cmd_params_free(free_arg);
+}
+
+static void read_settings(void)
+{
+       /* preserve the valid values */
+       char *old_term_charset = g_strdup(term_charset);
+       char *old_recode_fallback = g_strdup(recode_fallback);
+       char *old_recode_out_default = g_strdup(recode_out_default);
+
+       if (settings_get_bool("recode_transliterate")) {
+               /* check if transliterations are supported in this system */
+               if (!is_valid_charset("ASCII")) {
+                       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                   TXT_CONVERSION_NO_TRANSLITS);
+                       settings_set_bool("recode_transliterate", FALSE);
+               }
+       }
+
+       if (recode_fallback)
+               g_free(recode_fallback);
+       recode_fallback = g_strdup(settings_get_str("recode_fallback"));
+       if (!is_valid_charset(recode_fallback)) {
+               signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_INVALID_CHARSET), recode_fallback);
+               g_free(recode_fallback);
+               recode_fallback = is_valid_charset(old_recode_fallback) ? g_strdup(old_recode_fallback) : "ISO8859-1";
+               settings_set_str("recode_fallback", recode_fallback);
+       }
+
+       if (term_charset)
+               g_free(term_charset);
+       term_charset = g_strdup(settings_get_str("term_charset"));
+       if (!is_valid_charset(term_charset)) {
+               g_free(term_charset);
+#if defined (HAVE_NL_LANGINFO) && defined(CODESET)
+               term_charset = is_valid_charset(old_term_charset) ? g_strdup(old_term_charset) : 
+                              *nl_langinfo(CODESET) != '\0' ? g_strdup(nl_langinfo(CODESET)) : 
+                              "ISO8859-1";
+#else
+               term_charset = is_valid_charset(old_term_charset) ? g_strdup(old_term_charset) : "ISO8859-1";
+#endif         
+               settings_set_str("term_charset", term_charset);
+               /* FIXME: move the check of term_charset into fe-text/term.c 
+                         it breaks the proper term_input_type 
+                         setup and reemitting of the signal is kludgy */
+               if (g_strcasecmp(term_charset, old_term_charset) != 0)
+                       signal_emit("setup changed", 0);
+       }
+       
+       if (recode_out_default)
+               g_free(recode_out_default);
+       recode_out_default = g_strdup(settings_get_str("recode_out_default_charset"));
+       if (recode_out_default != NULL && *recode_out_default != '\0' && 
+           !is_valid_charset(recode_out_default)) {
+               signal_emit("error command", 2, GINT_TO_POINTER(CMDERR_INVALID_CHARSET), recode_out_default);
+               g_free(recode_out_default);
+               recode_out_default = is_valid_charset(old_recode_out_default) ? g_strdup(old_recode_out_default) : NULL;
+               settings_set_str("recode_out_default_charset", recode_out_default);
+       }
+
+       g_free(old_term_charset);
+       g_free(old_recode_fallback);
+       g_free(old_recode_out_default);
+}
+#endif
+
+void fe_recode_init (void)
+{
+/* FIXME: print this is not supported instead */
+#ifdef HAVE_GLIB2
+       command_bind("recode", NULL, (SIGNAL_FUNC) fe_recode_cmd);
+       command_bind("recode add", NULL, (SIGNAL_FUNC) fe_recode_add_cmd);
+       command_bind("recode remove", NULL, (SIGNAL_FUNC) fe_recode_remove_cmd);
+       signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+       read_settings();
+#endif
+}
+
+void fe_recode_deinit (void)
+{
+/* FIXME: print this is not supported instead */
+#ifdef HAVE_GLIB2
+       command_unbind("recode", (SIGNAL_FUNC) fe_recode_cmd);
+       command_unbind("recode add", (SIGNAL_FUNC) fe_recode_add_cmd);
+       command_unbind("recode remove", (SIGNAL_FUNC) fe_recode_remove_cmd);
+       signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
+#endif
+}
diff --git a/apps/irssi/src/fe-common/core/fe-recode.h b/apps/irssi/src/fe-common/core/fe-recode.h
new file mode 100644 (file)
index 0000000..9fc7328
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __FE_RECODE_H
+#define __FE_RECODE_H
+
+void fe_recode_init (void);
+void fe_recode_deinit (void);
+
+#endif /* __FE_RECODE_H */
index fdc676af9e3e34e44552550c36408d6b47b72308..5ecacbbc871f49b8d25196a74838fca4ebaac303 100644 (file)
@@ -147,6 +147,32 @@ static void cmd_server_add(const char *data)
        if (g_hash_table_lookup(optlist, "ssl"))
                rec->use_ssl = TRUE;
 
+       value = g_hash_table_lookup(optlist, "ssl_cert");
+       if (value != NULL && *value != '\0')
+               rec->ssl_cert = g_strdup(value);
+
+       value = g_hash_table_lookup(optlist, "ssl_pkey");
+       if (value != NULL && *value != '\0')
+               rec->ssl_pkey = g_strdup(value);
+
+       if (g_hash_table_lookup(optlist, "ssl_verify"))
+               rec->ssl_verify = TRUE;
+
+       value = g_hash_table_lookup(optlist, "ssl_cafile");
+       if (value != NULL && *value != '\0')
+               rec->ssl_cafile = g_strdup(value);
+
+       value = g_hash_table_lookup(optlist, "ssl_capath");
+       if (value != NULL && *value != '\0')
+               rec->ssl_capath = g_strdup(value);
+
+       if ((rec->ssl_cafile != NULL && rec->ssl_cafile[0] != '\0')
+       ||  (rec->ssl_capath != NULL && rec->ssl_capath[0] != '\0'))
+               rec->ssl_verify = TRUE;
+
+       if ((rec->ssl_cert != NULL && rec->ssl_cert[0] != '\0') || rec->ssl_verify == TRUE)
+               rec->use_ssl = TRUE;
+
        if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
        if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
        if (g_hash_table_lookup(optlist, "proxy")) rec->no_proxy = FALSE;
@@ -258,7 +284,9 @@ static void sig_server_connecting(SERVER_REC *server, IPADDR *ip)
        else
                net_ip2host(ip, ipaddr);
 
-       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONNECTING,
+       printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
+                   !server->connrec->reconnecting ?
+                   TXT_CONNECTING : TXT_RECONNECTING,
                    server->connrec->address, ipaddr, server->connrec->port);
 }
 
@@ -343,7 +371,7 @@ void fe_server_init(void)
        command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
        command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
        command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
-       command_set_options("server add", "4 6 ssl auto noauto proxy noproxy -host -port");
+       command_set_options("server add", "4 6 ssl +ssl_cert +ssl_pkey ssl_verify +ssl_cafile +ssl_capath auto noauto proxy noproxy -host -port");
 
        signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
        signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
index 7fb512e8446172150bad00929e60478e0b626497..dccfcba9105d4f95a6dbb1e002271ace75bf0155 100644 (file)
@@ -45,6 +45,9 @@ static void set_print(SETTINGS_REC *rec)
                value = value_int;
                break;
        case SETTING_TYPE_STRING:
+       case SETTING_TYPE_TIME:
+       case SETTING_TYPE_LEVEL:
+       case SETTING_TYPE_SIZE:
                value = settings_get_str(rec->key);
                break;
        default:
@@ -83,12 +86,15 @@ static void cmd_set(char *data)
        clear = g_hash_table_lookup(optlist, "clear") != NULL;
        set_default = g_hash_table_lookup(optlist, "default") != NULL;
 
+       if (*key == '\0')
+               clear = set_default = FALSE;
+
        last_section = ""; found = 0;
        sets = settings_get_sorted();
        for (tmp = sets; tmp != NULL; tmp = tmp->next) {
                SETTINGS_REC *rec = tmp->data;
 
-               if (((clear || *value != '\0') && g_strcasecmp(rec->key, key) != 0) ||
+               if (((clear || set_default || *value != '\0') && g_strcasecmp(rec->key, key) != 0) ||
                    (*value == '\0' && *key != '\0' && stristr(rec->key, key) == NULL))
                        continue;
 
@@ -106,20 +112,38 @@ static void cmd_set(char *data)
                                 if (clear)
                                        settings_set_bool(key, FALSE);
                                else if (set_default)
-                                       settings_set_bool(key, GPOINTER_TO_INT(rec->def));
+                                       settings_set_bool(key, rec->default_value.v_bool);
                                else
                                        set_boolean(key, value);
                                break;
                        case SETTING_TYPE_INT:
                                settings_set_int(key, clear ? 0 :
-                                                set_default ? GPOINTER_TO_INT(rec->def) :
+                                                set_default ? rec->default_value.v_int :
                                                 atoi(value));
                                break;
                        case SETTING_TYPE_STRING:
                                settings_set_str(key, clear ? "" :
-                                                set_default ? rec->def :
+                                                set_default ? rec->default_value.v_string :
                                                 value);
                                break;
+                       case SETTING_TYPE_TIME:
+                               if (!settings_set_time(key, clear ? "0" :
+                                                      set_default ? rec->default_value.v_string : value))
+                                       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                                   TXT_INVALID_TIME);
+                               break;
+                       case SETTING_TYPE_LEVEL:
+                               if (!settings_set_level(key, clear ? "" :
+                                                       set_default ? rec->default_value.v_string : value))
+                                       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                                   TXT_INVALID_LEVEL);
+                               break;
+                       case SETTING_TYPE_SIZE:
+                               if (!settings_set_size(key, clear ? "0" :
+                                                      set_default ? rec->default_value.v_string : value))
+                                       printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                                   TXT_INVALID_SIZE);
+                               break;
                        }
                        signal_emit("setup changed", 0);
                }
index dc264d657a5ac47a7433f0395d8fc8e80cb6e92b..06621c776a2e83b7eaf435cff6ffc4456505d62c 100644 (file)
@@ -70,7 +70,7 @@ WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic)
 
        rec = g_new0(WINDOW_REC, 1);
        rec->refnum = window_get_new_refnum();
-       rec->level = level2bits(settings_get_str("window_default_level"));
+       rec->level = settings_get_level("window_default_level");
 
        windows = g_slist_prepend(windows, rec);
        signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic));
@@ -308,8 +308,11 @@ WINDOW_REC *window_find_closest(void *server, const char *name, int level)
                        /* match, but if multiple windows have the same level
                           we could be choosing a bad one here, eg.
                           name=nick1 would get nick2's query instead of
-                          generic msgs window. */
-                       if (g_strcasecmp(name, item->visible_name) == 0)
+                          generic msgs window.
+
+                          And check for prefixed !channel name --Borys  */
+                       if (g_strcasecmp(name, item->visible_name) == 0 ||
+                           g_strcasecmp(name, (char *) window_item_get_target((WI_ITEM_REC *) item)) == 0)
                                return namewindow;
                }
        }
@@ -646,7 +649,7 @@ void windows_init(void)
        settings_add_bool("lookandfeel", "window_auto_change", FALSE);
        settings_add_bool("lookandfeel", "windows_auto_renumber", TRUE);
        settings_add_bool("lookandfeel", "window_check_level_first", FALSE);
-       settings_add_str("lookandfeel", "window_default_level", "NONE");
+       settings_add_level("lookandfeel", "window_default_level", "NONE");
 
        read_settings();
        signal_add("server looking", (SIGNAL_FUNC) sig_server_connected);
index 064648941322401de74050619ef0be22579de55a..ccfad968be7cb82adad035786b8262f7c84d13c5 100644 (file)
 #include "formats.h"
 #include "themes.h"
 #include "translation.h"
+#ifdef HAVE_GLIB2
+#include "recode.h"
+#include "utf8.h"
+#endif
 
 static const char *format_backs = "04261537";
 static const char *format_fores = "kbgcrmyw";
@@ -66,13 +70,13 @@ static void format_expand_code(const char **format, GString *out, int *flags)
        int set;
 
        if (flags == NULL) {
-                /* flags are being ignored - skip the code */
+               /* flags are being ignored - skip the code */
                while (**format != ']')
                        (*format)++;
-                return;
+               return;
        }
 
-        set = TRUE;
+       set = TRUE;
        (*format)++;
        while (**format != ']' && **format != '\0') {
                if (**format == '+')
@@ -94,7 +98,7 @@ static void format_expand_code(const char **format, GString *out, int *flags)
                                (*format)++;
                        }
                        g_string_append_c(out, ',');
-                        (*format)--;
+                       (*format)--;
                        break;
                case 's':
                case 'S':
@@ -104,12 +108,12 @@ static void format_expand_code(const char **format, GString *out, int *flags)
                        break;
                case 't':
                        *flags |= set ? PRINT_FLAG_SET_TIMESTAMP :
-                                PRINT_FLAG_UNSET_TIMESTAMP;
-                        break;
+                               PRINT_FLAG_UNSET_TIMESTAMP;
+                       break;
                case 'T':
                        *flags |= set ? PRINT_FLAG_SET_SERVERTAG :
-                                PRINT_FLAG_UNSET_SERVERTAG;
-                        break;
+                               PRINT_FLAG_UNSET_SERVERTAG;
+                       break;
                }
 
                (*format)++;
@@ -120,12 +124,12 @@ int format_expand_styles(GString *out, const char **format, int *flags)
 {
        char *p, fmt;
 
-        fmt = **format;
+       fmt = **format;
        switch (fmt) {
        case '{':
        case '}':
        case '%':
-                /* escaped char */
+               /* escaped char */
                g_string_append_c(out, fmt);
                break;
        case 'U':
@@ -165,7 +169,7 @@ int format_expand_styles(GString *out, const char **format, int *flags)
                g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
                break;
        case '>':
-                /* clear to end of line */
+               /* clear to end of line */
                g_string_append_c(out, 4);
                g_string_append_c(out, FORMAT_STYLE_CLRTOEOL);
                break;
@@ -175,7 +179,7 @@ int format_expand_styles(GString *out, const char **format, int *flags)
                break;
        case '[':
                /* code */
-                format_expand_code(format, out, flags);
+               format_expand_code(format, out, flags);
                break;
        default:
                /* check if it's a background color */
@@ -219,10 +223,10 @@ void format_read_arglist(va_list va, FORMAT_REC *format,
 {
        int num, len, bufpos;
 
-        g_return_if_fail(format->params < arglist_size);
+       g_return_if_fail(format->params < arglist_size);
 
        bufpos = 0;
-        arglist[format->params] = NULL;
+       arglist[format->params] = NULL;
        for (num = 0; num < format->params; num++) {
                switch (format->paramtypes[num]) {
                case FORMAT_STRING:
@@ -255,7 +259,7 @@ void format_read_arglist(va_list va, FORMAT_REC *format,
                        arglist[num] = buffer+bufpos;
                        len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
                                         "%ld", l);
-                        bufpos += len+1;
+                       bufpos += len+1;
                        break;
                }
                case FORMAT_FLOAT: {
@@ -275,19 +279,18 @@ void format_read_arglist(va_list va, FORMAT_REC *format,
                }
        }
 }
-
 void format_create_dest(TEXT_DEST_REC *dest,
                        void *server, const char *target,
                        int level, WINDOW_REC *window)
 {
-        format_create_dest_tag(dest, server, NULL, target, level, window);
+       format_create_dest_tag(dest, server, NULL, target, level, window);
 }
 
 void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
                            const char *server_tag, const char *target,
                            int level, WINDOW_REC *window)
 {
-        memset(dest, 0, sizeof(TEXT_DEST_REC));
+       memset(dest, 0, sizeof(TEXT_DEST_REC));
 
        dest->server = server;
        dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag;
@@ -296,23 +299,46 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
        dest->window = window != NULL ? window :
                window_find_closest(server, target, level);
 }
+#ifdef HAVE_GLIB2
+static int advance (char const **str, gboolean utf8)
+{
+       if (utf8) {
+               gunichar c;
+
+               c = g_utf8_get_char(*str);
+               *str = g_utf8_next_char(*str);
 
+               return utf8_width(c);
+       } else {
+               *str += 1;
+
+               return 1;
+       }
+}
+#endif
 /* Return length of text part in string (ie. without % codes) */
 int format_get_length(const char *str)
 {
-        GString *tmp;
+       GString *tmp;
        int len;
+#ifdef HAVE_GLIB2
+       gboolean utf8;
+#endif
 
-        g_return_val_if_fail(str != NULL, 0);
+       g_return_val_if_fail(str != NULL, 0);
 
-        tmp = g_string_new(NULL);
+#ifdef HAVE_GLIB2
+       utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
+#endif
+
+       tmp = g_string_new(NULL);
        len = 0;
        while (*str != '\0') {
                if (*str == '%' && str[1] != '\0') {
                        str++;
                        if (*str != '%' &&
                            format_expand_styles(tmp, &str, NULL)) {
-                                str++;
+                               str++;
                                continue;
                        }
 
@@ -320,13 +346,16 @@ int format_get_length(const char *str)
                        if (*str != '%')
                                len++;
                }
-
-                len++;
+#ifdef HAVE_GLIB2
+               len += advance(&str, utf8);
+#else
+         len++;
                str++;
+#endif
        }
 
        g_string_free(tmp, TRUE);
-        return len;
+       return len;
 }
 
 /* Return how many characters in `str' must be skipped before `len'
@@ -336,34 +365,44 @@ int format_real_length(const char *str, int len)
 {
        GString *tmp;
        const char *start;
-
-        g_return_val_if_fail(str != NULL, 0);
-        g_return_val_if_fail(len >= 0, 0);
-
-        start = str;
-        tmp = g_string_new(NULL);
+#ifdef HAVE_GLIB2
+       gboolean utf8;
+#endif
+       g_return_val_if_fail(str != NULL, 0);
+       g_return_val_if_fail(len >= 0, 0);
+
+#ifdef HAVE_GLIB2
+       utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
+#endif
+
+       start = str;
+       tmp = g_string_new(NULL);
        while (*str != '\0' && len > 0) {
                if (*str == '%' && str[1] != '\0') {
                        str++;
                        if (*str != '%' &&
                            format_expand_styles(tmp, &str, NULL)) {
-                                str++;
+                               str++;
                                continue;
                        }
 
                        /* %% or unknown %code, written as-is */
                        if (*str != '%') {
                                if (--len == 0)
-                                        break;
+                                       break;
                        }
                }
 
-                len--;
+#ifdef HAVE_GLIB2
+               len -= advance(&str, utf8);
+#else
+         len--;
                str++;
+#endif
        }
 
        g_string_free(tmp, TRUE);
-        return (int) (str-start);
+       return (int) (str-start);
 }
 
 char *format_string_expand(const char *text, int *flags)
@@ -371,7 +410,7 @@ char *format_string_expand(const char *text, int *flags)
        GString *out;
        char code, *ret;
 
-        g_return_val_if_fail(text != NULL, NULL);
+       g_return_val_if_fail(text != NULL, NULL);
 
        out = g_string_new(NULL);
 
@@ -432,7 +471,7 @@ static char *format_get_text_args(TEXT_DEST_REC *dest,
                        if (ret != NULL) {
                                /* string shouldn't end with \003 or it could
                                   mess up the next one or two characters */
-                                int diff;
+                               int diff;
                                int len = strlen(ret);
                                while (len > 0 && ret[len-1] == 3) len--;
                                diff = strlen(ret)-len;
@@ -502,7 +541,7 @@ char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
        if (module_theme == NULL)
                return NULL;
 
-        text = module_theme->expanded_formats[formatnum];
+       text = module_theme->expanded_formats[formatnum];
        return format_get_text_args(dest, text, args);
 }
 
@@ -548,7 +587,7 @@ char *format_add_linestart(const char *text, const char *linestart)
 
        ret = str->str;
        g_string_free(str, FALSE);
-        return ret;
+       return ret;
 }
 
 char *format_add_lineend(const char *text, const char *linestart)
@@ -573,7 +612,7 @@ char *format_add_lineend(const char *text, const char *linestart)
 
        ret = str->str;
        g_string_free(str, FALSE);
-        return ret;
+       return ret;
 }
 
 #define LINE_START_IRSSI_LEVEL \
@@ -589,16 +628,16 @@ char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
 {
        int format;
 
-        /* check for flags if we want to override defaults */
+       /* check for flags if we want to override defaults */
        if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
                return NULL;
 
        if (dest->flags & PRINT_FLAG_SET_LINE_START)
                format = TXT_LINE_START;
-        else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
+       else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
                format = TXT_LINE_START_IRSSI;
        else {
-                /* use defaults */
+               /* use defaults */
                if (dest->level & LINE_START_IRSSI_LEVEL)
                        format = TXT_LINE_START_IRSSI;
                else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
@@ -612,20 +651,20 @@ char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
 
 static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
 {
-        char *format, str[256];
+       char *format, str[256];
        struct tm *tm;
        int diff;
 
        if ((timestamp_level & dest->level) == 0)
                return NULL;
 
-        /* check for flags if we want to override defaults */
+       /* check for flags if we want to override defaults */
        if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
                return NULL;
 
        if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
            (dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
-                return NULL;
+               return NULL;
 
 
        if (timestamp_timeout > 0) {
@@ -639,7 +678,7 @@ static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
        format = format_get_text_theme(theme, MODULE_NAME, dest,
                                       TXT_TIMESTAMP);
        if (strftime(str, sizeof(str), format, tm) <= 0)
-                str[0] = '\0';
+               str[0] = '\0';
        g_free(format);
        return g_strdup(str);
 }
@@ -649,9 +688,9 @@ static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
        int count = 0;
 
        if (dest->server_tag == NULL || hide_server_tags)
-                return NULL;
+               return NULL;
 
-        /* check for flags if we want to override defaults */
+       /* check for flags if we want to override defaults */
        if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
                return NULL;
 
@@ -672,7 +711,7 @@ static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
                }
 
                if (count < 2)
-                        return NULL;
+                       return NULL;
        }
 
        return format_get_text_theme(theme, MODULE_NAME, dest,
@@ -796,7 +835,7 @@ static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
                /* foreground color */
                if (**str != ',') {
                        fg = **str-'0';
-                        (*str)++;
+                       (*str)++;
                        if (i_isdigit(**str)) {
                                fg = fg*10 + (**str-'0');
                                (*str)++;
@@ -833,7 +872,7 @@ int strip_real_length(const char *str, int len,
 {
        const char *start = str;
 
-        if (last_color_pos != NULL)
+       if (last_color_pos != NULL)
                *last_color_pos = -1;
        if (last_color_len != NULL)
                *last_color_len = -1;
@@ -844,25 +883,25 @@ int strip_real_length(const char *str, int len,
 
                        if (last_color_pos != NULL)
                                *last_color_pos = (int) (str-start);
-                        str++;
+                       str++;
                        get_mirc_color(&str, NULL, NULL);
-                        if (last_color_len != NULL)
+                       if (last_color_len != NULL)
                                *last_color_len = (int) (str-mircstart);
 
                } else if (*str == 4 && str[1] != '\0') {
                        if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
                                if (last_color_pos != NULL)
                                        *last_color_pos = (int) (str-start);
-                                if (last_color_len != NULL)
-                                        *last_color_len = 3;
+                               if (last_color_len != NULL)
+                                       *last_color_len = 3;
                                str++;
                        } else if (str[1] == FORMAT_STYLE_DEFAULTS) {
                                if (last_color_pos != NULL)
                                        *last_color_pos = (int) (str-start);
-                                if (last_color_len != NULL)
-                                        *last_color_len = 2;
+                               if (last_color_len != NULL)
+                                       *last_color_len = 2;
                        }
-                        str += 2;
+                       str += 2;
                } else {
                        if (!IS_COLOR_CODE(*str)) {
                                if (len-- == 0)
@@ -877,61 +916,61 @@ int strip_real_length(const char *str, int len,
 
 char *strip_codes(const char *input)
 {
-        const char *p;
-        char *str, *out;
-
-        out = str = g_strdup(input);
-        for (p = input; *p != '\0'; p++) {
-                if (*p == 3) {
-                        p++;  
-
-                        /* mirc color */
-                        get_mirc_color(&p, NULL, NULL);
-                        p--;
-                        continue;
-                }
-
-                if (*p == 4 && p[1] != '\0') {
-                        if (p[1] >= FORMAT_STYLE_SPECIAL) {
-                                p++;
-                                continue;
-                        }
-
-                        /* irssi color */
-                        if (p[2] != '\0') {
-                                p += 2;
-                                continue;
-                        }
+       const char *p;
+       char *str, *out;
+
+       out = str = g_strdup(input);
+       for (p = input; *p != '\0'; p++) {
+               if (*p == 3) {
+                       p++;
+
+                       /* mirc color */
+                       get_mirc_color(&p, NULL, NULL);
+                       p--;
+                       continue;
+               }
+
+               if (*p == 4 && p[1] != '\0') {
+                       if (p[1] >= FORMAT_STYLE_SPECIAL) {
+                               p++;
+                               continue;
+                       }
+
+                       /* irssi color */
+                       if (p[2] != '\0') {
+                               p += 2;
+                               continue;
+                       }
                }
 
                if (*p == 27 && p[1] != '\0') {
-                        p++;
+                       p++;
                        p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
                        p--;
                } else if (!IS_COLOR_CODE(*p))
-                        *out++ = *p;   
-        }
+                       *out++ = *p;
+       }
 
-        *out = '\0';
-        return str; 
+       *out = '\0';
+       return str;
 }
 
 /* send a fully parsed text string for GUI to print */
 void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
 {
-        THEME_REC *theme;
+       THEME_REC *theme;
        char *dup, *str, *ptr, type;
        int fgcolor, bgcolor;
        int flags;
 
-        theme = dest->window != NULL && dest->window->theme != NULL ?
+       theme = dest->window != NULL && dest->window->theme != NULL ?
                dest->window->theme : current_theme;
 
        dup = str = g_strdup(text);
 
        flags = 0; fgcolor = theme->default_color; bgcolor = -1;
        while (*str != '\0') {
-                type = '\0';
+               type = '\0';
                for (ptr = str; *ptr != '\0'; ptr++) {
                        if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
                                type = *ptr;
@@ -945,14 +984,14 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
                if (type == 7) {
                        /* bell */
                        if (settings_get_bool("bell_beeps"))
-                                signal_emit("beep", 0);
+                               signal_emit("beep", 0);
                } else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
-                        /* clear to end of line */
+                       /* clear to end of line */
                        flags |= GUI_PRINT_FLAG_CLRTOEOL;
                }
 
                if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
-                        /* send the text to gui handler */
+                       /* send the text to gui handler */
                        signal_emit_id(signal_gui_print_text, 6, dest->window,
                                       GINT_TO_POINTER(fgcolor),
                                       GINT_TO_POINTER(bgcolor),
@@ -1009,20 +1048,20 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
                                while (*ptr != ',' && *ptr != '\0')
                                        ptr++;
                                if (*ptr != '\0') *ptr++ = '\0';
-                                ptr--;
+                               ptr--;
                                signal_emit_id(signal_gui_print_text, 6,
                                               dest->window, NULL, NULL,
                                               GINT_TO_POINTER(GUI_PRINT_FLAG_INDENT_FUNC),
-                                              str, start, dest);
+                                              start, dest);
                                break;
                        }
                        case FORMAT_STYLE_DEFAULTS:
-                                fgcolor = theme->default_color;
+                               fgcolor = theme->default_color;
                                bgcolor = -1;
                                flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
                                break;
                        case FORMAT_STYLE_CLRTOEOL:
-                                break;
+                               break;
                        default:
                                if (*ptr != FORMAT_COLOR_NOCHANGE) {
                                        fgcolor = (unsigned char) *ptr-'0';
@@ -1044,8 +1083,8 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
                                                flags &= ~GUI_PRINT_FLAG_BLINK;
                                        else {
                                                /* blink */
-                                                bgcolor -= 8;
-                                                flags |= GUI_PRINT_FLAG_BLINK;
+                                               bgcolor -= 8;
+                                               flags |= GUI_PRINT_FLAG_BLINK;
                                        }
                                }
                        }
@@ -1091,11 +1130,9 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
 static void read_settings(void)
 {
        timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
-       if (timestamp_level > 0) {
-               timestamp_level =
-                       level2bits(settings_get_str("timestamp_level"));
-       }
-       timestamp_timeout = settings_get_int("timestamp_timeout");
+       if (timestamp_level > 0)
+               timestamp_level = settings_get_level("timestamp_level");
+       timestamp_timeout = settings_get_time("timestamp_timeout")/1000;
 
        hide_server_tags = settings_get_bool("hide_server_tags");
        hide_text_style = settings_get_bool("hide_text_style");
index 9749504b4afa107f49800622a98216375346f552..4a913ee6535b0137eeb088613113287d47e95d3b 100644 (file)
@@ -124,8 +124,8 @@ static void hilight_init_rec(HILIGHT_REC *rec)
 void hilight_create(HILIGHT_REC *rec)
 {
        if (g_slist_find(hilights, rec) != NULL) {
-               hilights = g_slist_remove(hilights, rec);
                hilight_remove_config(rec);
+               hilights = g_slist_remove(hilights, rec);
        }
 
        hilights = g_slist_append(hilights, rec);
@@ -279,7 +279,7 @@ static char *hilight_get_act_color(HILIGHT_REC *rec)
                        settings_get_str("hilight_act_color"));
 }
 
-static char *hilight_get_color(HILIGHT_REC *rec)
+char *hilight_get_color(HILIGHT_REC *rec)
 {
        const char *color;
 
@@ -291,7 +291,7 @@ static char *hilight_get_color(HILIGHT_REC *rec)
        return format_string_expand(color, NULL);
 }
 
-static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
+void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
 {
        dest->level |= MSGLEVEL_HILIGHT;
 
@@ -305,6 +305,8 @@ static void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
                dest->hilight_color = hilight_get_act_color(rec);
 }
 
+static void hilight_print(int index, HILIGHT_REC *rec);
+
 static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
                           const char *stripped)
 {
@@ -398,19 +400,15 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
        signal_stop();
 }
 
-char *hilight_match_nick(SERVER_REC *server, const char *channel,
+HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
                         const char *nick, const char *address,
                         int level, const char *msg)
 {
         HILIGHT_REC *rec;
-       char *color;
 
        rec = hilight_match(server, channel, nick, address,
                            level, msg, NULL, NULL);
-       color = rec == NULL || !rec->nick ? NULL :
-               hilight_get_color(rec);
-
-       return color;
+       return (rec == NULL || !rec->nick) ? NULL : rec;
 }
 
 static void read_hilight_config(void)
@@ -491,8 +489,6 @@ static void hilight_print(int index, HILIGHT_REC *rec)
 #endif
        }
 
-       if (options->len > 1) g_string_truncate(options, options->len-1);
-
        if (rec->priority != 0)
                g_string_sprintfa(options, "-priority %d ", rec->priority);
        if (rec->color != NULL)
@@ -504,6 +500,8 @@ static void hilight_print(int index, HILIGHT_REC *rec)
                g_strjoinv(",", rec->channels);
        levelstr = rec->level == 0 ? NULL :
                bits2level(rec->level);
+       if (levelstr != NULL)
+               levelstr = g_strconcat(levelstr, " ", NULL);
        printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
                    TXT_HILIGHT_LINE, index, rec->text,
                    chans != NULL ? chans : "",
@@ -676,14 +674,14 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
 
 static void read_settings(void)
 {
-       default_hilight_level = level2bits(settings_get_str("hilight_level"));
+       default_hilight_level = settings_get_level("hilight_level");
 }
 
 void hilight_text_init(void)
 {
        settings_add_str("lookandfeel", "hilight_color", "%Y");
        settings_add_str("lookandfeel", "hilight_act_color", "%M");
-       settings_add_str("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
+       settings_add_level("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
 
         read_settings();
 
index 6e5107374ebc98ab268a504177e2ad0859927218..74c58780c2ffb63dc7fa2185831f135be7989a32 100644 (file)
@@ -5,6 +5,8 @@
 #  include <regex.h>
 #endif
 
+#include "formats.h"
+
 typedef struct _HILIGHT_REC HILIGHT_REC;
 
 struct _HILIGHT_REC {
@@ -36,9 +38,12 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
                           int level, const char *str,
                           int *match_beg, int *match_end);
 
-char *hilight_match_nick(SERVER_REC *server, const char *channel,
+HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
                         const char *nick, const char *address,
                         int level, const char *msg);
+                        
+char *hilight_get_color(HILIGHT_REC *rec);
+void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec);
 
 void hilight_create(HILIGHT_REC *rec);
 void hilight_remove(HILIGHT_REC *rec);
index 9bbb218aa47cb0b589a9b9522d3ed7c92e668147..717bed1f6248ab6602559dc4b0dbc0f9d4c84d9e 100644 (file)
@@ -551,8 +551,6 @@ static int key_states_search(const unsigned char *combo,
         return 0;
 }
 
-/* Returns TRUE if key press was consumed. Control characters should be sent
-   as "^@" .. "^_" instead of #0..#31 chars, #127 should be sent as ^? */
 int key_pressed(KEYBOARD_REC *keyboard, const char *key)
 {
        KEY_REC *rec;
@@ -565,7 +563,7 @@ int key_pressed(KEYBOARD_REC *keyboard, const char *key)
        if (keyboard->key_state == NULL && key[1] == '\0' &&
            !used_keys[(int) (unsigned char) key[0]]) {
                /* fast check - key not used */
-               return FALSE;
+               return -1;
        }
 
         first_key = keyboard->key_state == NULL;
@@ -583,13 +581,13 @@ int key_pressed(KEYBOARD_REC *keyboard, const char *key)
                /* unknown key combo, eat the invalid key
                   unless it was the first key pressed */
                 g_free(combo);
-               return !first_key;
+               return first_key ? -1 : 1;
        }
 
        if (g_tree_lookup(key_states, combo) != rec) {
                /* key combo continues.. */
                keyboard->key_state = combo;
-                return TRUE;
+                return 0;
        }
 
         /* finished key combo, execute */
@@ -597,7 +595,7 @@ int key_pressed(KEYBOARD_REC *keyboard, const char *key)
        consumed = key_emit_signal(keyboard, rec);
 
        /* never consume non-control characters */
-       return consumed;
+       return consumed ? 1 : -1;
 }
 
 void keyboard_entry_redirect(SIGNAL_FUNC func, const char *entry,
index eff8966a37d6c2f770b77ac8beec8bad8fa2ac93..970dc7b75bf2983f4a10e3324f99f52aff03faa5 100644 (file)
@@ -29,8 +29,9 @@ extern GSList *keyinfos;
 KEYBOARD_REC *keyboard_create(void *gui_data);
 /* Destroys a keyboard */
 void keyboard_destroy(KEYBOARD_REC *keyboard);
-/* Returns TRUE if key press was consumed. Control characters should be sent
-   as "^@" .. "^_" instead of #0..#31 chars, #127 should be sent as ^? */
+/* Returns 1 if key press was consumed, -1 if not, 0 if it's beginning of a
+   key combo. Control characters should be sent as "^@" .. "^_" instead of
+   #0..#31 chars, #127 should be sent as ^? */
 int key_pressed(KEYBOARD_REC *keyboard, const char *key);
 
 void key_bind(const char *id, const char *description,
index 2d4f02e1a33f0026165a497505f0d51df943c6bf..cae4b9a584482392e1fe2306e6f7f5db625fd272 100644 (file)
@@ -71,6 +71,7 @@ FORMAT_REC fecommon_core_formats[] = {
 
        { "looking_up", "Looking up {server $0}", 1, { 0 } },
        { "connecting", "Connecting to {server $0} [$1] port {hilight $2}", 3, { 0, 0, 1 } },
+       { "reconnecting", "Reconnecting to {server $0} [$1] port {hilight $2} - use /RMRECONNS to abort", 3, { 0, 0, 1 } },
        { "connection_established", "Connection to {server $0} established", 1, { 0 } },
        { "cant_connect", "Unable to connect server {server $0} port {hilight $1} {reason $2}", 3, { 0, 1, 0 } },
        { "connection_lost", "Connection lost to {server $0}", 1, { 0 } },
@@ -214,6 +215,12 @@ FORMAT_REC fecommon_core_formats[] = {
        { "chan_not_synced", "Channel not fully synchronized yet, try again after a while", 0 },
        { "illegal_proto", "Command isn't designed for the chat protocol of the active server", 0 },
        { "not_good_idea", "Doing this is not a good idea. Add -YES option to command if you really mean it", 0 },
+       { "invalid_time", "Invalid timestamp", 0 },
+       { "invalid_level", "Invalid message level", 0 },
+       { "invalid_size", "Invalid size", 0 },
+       { "invalid_charset", "Invalid charset: $0", 1, { 0 } },
+       { "eval_max_recurse", "/eval hit maximum recursion limit", 0 },
+       { "program_not_found", "Could not find file or file is not executable", 0 },
 
        /* ---- */
        { NULL, "Themes", 0 },
@@ -242,11 +249,22 @@ FORMAT_REC fecommon_core_formats[] = {
        { "ignore_line", "%#$[-4]0 $1: $2 $3 $4", 4, { 1, 0, 0, 0 } },
        { "ignore_footer", "", 0 },
 
+       /* ---- */
+       { NULL, "Recode", 0 },
+
+       { "not_channel_or_query", "The current window is not a channel or query window", 0 },
+       { "conversion_added", "Added {hilight $0}/{hilight $1} to conversion database", 2, { FORMAT_STRING, FORMAT_STRING } },
+       { "conversion_removed", "Removed {hilight $0} from conversion database", 1, { FORMAT_STRING } },
+       { "conversion_not_found", "{hilight $0} not found in conversion database", 1, { FORMAT_STRING } },
+       { "conversion_no_translits", "Transliterations not supported in this system", 0 },
+       { "recode_header", "%#Target                         Character set", 0 },
+       { "recode_line", "%#%|$[!30]0 $1", 2, { FORMAT_STRING, FORMAT_STRING } },
+
        /* ---- */
        { NULL, "Misc", 0 },
 
        { "unknown_chat_protocol", "Unknown chat protocol: $0", 1, { 0 } },
-       { "unknown_chatnet", "Unknown chat network: $0 (create it with /IRCNET ADD)", 1, { 0 } },
+       { "unknown_chatnet", "Unknown chat network: $0 (create it with /NETWORK ADD)", 1, { 0 } },
        { "not_toggle", "Value must be either ON, OFF or TOGGLE", 0 },
        { "perl_error", "Perl error: $0", 1, { 0 } },
        { "bind_header", "%#Key                  Action", 0 },
index 7cd46bab956fc53b772a32fd1808dfaf4af2a6cf..135378bab07e38fb3e953bc795eb507e2f83cfa8 100644 (file)
@@ -48,7 +48,8 @@ enum {
 
        TXT_LOOKING_UP,
        TXT_CONNECTING,
-       TXT_CONNECTION_ESTABLISHED,
+       TXT_RECONNECTING,
+        TXT_CONNECTION_ESTABLISHED,
        TXT_CANT_CONNECT,
        TXT_CONNECTION_LOST,
        TXT_LAG_DISCONNECTED,
@@ -183,6 +184,12 @@ enum {
        TXT_CHAN_NOT_SYNCED,
         TXT_ILLEGAL_PROTO,
        TXT_NOT_GOOD_IDEA,
+       TXT_INVALID_TIME,
+       TXT_INVALID_LEVEL,
+       TXT_INVALID_SIZE,
+       TXT_INVALID_CHARSET,
+       TXT_EVAL_MAX_RECURSE,
+       TXT_PROGRAM_NOT_FOUND,
 
        TXT_FILL_11,
 
@@ -211,6 +218,16 @@ enum {
 
        TXT_FILL_13,
 
+       TXT_NOT_CHANNEL_OR_QUERY,
+       TXT_CONVERSION_ADDED,
+       TXT_CONVERSION_REMOVED,
+       TXT_CONVERSION_NOT_FOUND,
+       TXT_CONVERSION_NO_TRANSLITS,
+       TXT_RECODE_HEADER,
+       TXT_RECODE_LINE,
+
+       TXT_FILL_14,
+
         TXT_UNKNOWN_CHAT_PROTOCOL,
         TXT_UNKNOWN_CHATNET,
        TXT_NOT_TOGGLE,
index 203e3a3015a6606814f6ccf71a46fdfa73ed9c00..51b61b3e34d3baa3bdeb12f80daf0007a9cd1155 100644 (file)
@@ -2,6 +2,7 @@
 
 #define MODULE_NAME "fe-common/core"
 
+typedef guint32 unichar;
 typedef struct {
        time_t time;
        char *nick;
index e32b5943258336f16af8acda5cbf8cbcf615b6f7..a3d34a22ace243ba688cbd56e154ee3d035bda6d 100644 (file)
@@ -163,13 +163,13 @@ static void print_line(TEXT_DEST_REC *dest, const char *text)
 
        g_return_if_fail(dest != NULL);
        g_return_if_fail(text != NULL);
-
+       
         theme = window_get_theme(dest->window);
        tmp = format_get_level_tag(theme, dest);
        str = !theme->info_eol ? format_add_linestart(text, tmp) :
                format_add_lineend(text, tmp);
        g_free_not_null(tmp);
-
+       
        /* send both the formatted + stripped (for logging etc.) */
        stripped = strip_codes(str);
        signal_emit_id(signal_print_text, 3, dest, str, stripped);
@@ -474,7 +474,7 @@ static void sig_gui_dialog(const char *type, const char *text)
 
 static void read_settings(void)
 {
-       beep_msg_level = level2bits(settings_get_str("beep_msg_level"));
+       beep_msg_level = settings_get_level("beep_msg_level");
        beep_when_away = settings_get_bool("beep_when_away");
         beep_when_window_active = settings_get_bool("beep_when_window_active");
 }
index 437b118faeccf36a8a08eb1b6823c39164ecec22..b4407bda9e6b521c3a1e17453793341eecbaae5a 100644 (file)
@@ -39,8 +39,9 @@ GHashTable *default_formats;
 
 static int init_finished;
 static char *init_errors;
+static THEME_REC *internal_theme;
 
-static int theme_read(THEME_REC *theme, const char *path, const char *data);
+static int theme_read(THEME_REC *theme, const char *path);
 
 THEME_REC *theme_create(const char *path, const char *name)
 {
@@ -309,7 +310,7 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
        int braces = 1; /* we start with one brace opened */
 
        str = g_string_new(NULL);
-       while ((**format != '\0') && (braces)) {
+       while (**format != '\0' && braces != 0) {
                if (**format == '{')
                        braces++;
                else if (**format == '}')
@@ -325,7 +326,7 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
                        continue;
                }
                
-               if (!braces) {
+               if (braces == 0) {
                        (*format)++;
                        break;
                }
@@ -775,6 +776,22 @@ void theme_unregister_module(const char *module)
        themes_remove_module(module);
 }
 
+void theme_set_default_abstract(const char *key, const char *value)
+{
+       gpointer oldkey, oldvalue;
+
+       if (g_hash_table_lookup_extended(internal_theme->abstracts, key,
+                                        &oldkey, &oldvalue)) {
+               /* new values override old ones */
+               g_hash_table_remove(internal_theme->abstracts, oldkey);
+               g_free(oldkey);
+               g_free(oldvalue);
+       }
+
+       g_hash_table_insert(internal_theme->abstracts,
+                           g_strdup(key), g_strdup(value));
+}
+
 static THEME_REC *theme_find(const char *name)
 {
        GSList *tmp;
@@ -840,7 +857,7 @@ THEME_REC *theme_load(const char *setname)
         oldtheme = theme;
        theme = theme_create(fname, name);
        theme->last_modify = statbuf.st_mtime;
-       if (!theme_read(theme, theme->path, NULL)) {
+       if (!theme_read(theme, theme->path)) {
                 /* error reading .theme file */
                theme_destroy(theme);
                theme = NULL;
@@ -848,6 +865,8 @@ THEME_REC *theme_load(const char *setname)
 
        if (oldtheme != NULL && theme != NULL) {
                theme_destroy(oldtheme);
+               if (current_theme == oldtheme)
+                       current_theme = theme;
                window_themes_update();
        }
 
@@ -856,6 +875,17 @@ THEME_REC *theme_load(const char *setname)
        return theme;
 }
 
+static void copy_abstract_hash(char *key, char *value, GHashTable *dest)
+{
+       g_hash_table_insert(dest, g_strdup(key), g_strdup(value));
+}
+
+static void theme_copy_abstracts(THEME_REC *dest, THEME_REC *src)
+{
+       g_hash_table_foreach(src->abstracts, (GHFunc) copy_abstract_hash,
+                            dest->abstracts);
+}
+
 typedef struct {
         THEME_REC *theme;
        CONFIG_REC *config;
@@ -882,13 +912,13 @@ static void read_error(const char *str)
        }
 }
 
-static int theme_read(THEME_REC *theme, const char *path, const char *data)
+static int theme_read(THEME_REC *theme, const char *path)
 {
        CONFIG_REC *config;
        THEME_READ_REC rec;
         char *str;
 
-       config = config_open(data == NULL ? path : NULL, -1) ;
+       config = config_open(path, -1) ;
        if (config == NULL) {
                /* didn't exist or no access? */
                str = g_strdup_printf("Error reading theme file %s: %s",
@@ -898,8 +928,8 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data)
                return FALSE;
        }
 
-       if (data != NULL)
-               config_parse_data(config, data, "internal");
+       if (path == NULL)
+               config_parse_data(config, default_theme, "internal");
         else
                config_parse(config);
 
@@ -920,15 +950,8 @@ static int theme_read(THEME_REC *theme, const char *path, const char *data)
                 theme->default_color = -1;
        theme_read_replaces(config, theme);
 
-       if (data == NULL) {
-               /* get the default abstracts from default theme. */
-               CONFIG_REC *default_config;
-
-               default_config = config_open(NULL, -1);
-               config_parse_data(default_config, default_theme, "internal");
-               theme_read_abstracts(default_config, theme);
-               config_close(default_config);
-       }
+       if (path != NULL)
+               theme_copy_abstracts(theme, internal_theme);
        theme_read_abstracts(config, theme);
 
        rec.theme = theme;
@@ -1226,12 +1249,13 @@ static void sig_complete_format(GList **list, WINDOW_REC *window,
         ptr = line;
 
        words = 0;
-       do {
-                ptr++;
-
-               words++;
-                ptr = strchr(ptr, ' ');
-       } while (ptr != NULL);
+       if (*ptr != '\0') {
+               do {
+                       ptr++;
+                       words++;
+                       ptr = strchr(ptr, ' ');
+               } while (ptr != NULL);
+       }
 
        if (words > 2)
                return;
@@ -1273,7 +1297,7 @@ static void read_settings(void)
                change_theme(theme, TRUE);
 }
 
-static void themes_read(void)
+void themes_reload(void)
 {
        GSList *refs;
        char *fname;
@@ -1296,7 +1320,7 @@ static void themes_read(void)
                fname = g_strdup_printf("%s/default.theme", get_irssi_dir());
                current_theme = theme_create(fname, "default");
                current_theme->default_color = -1;
-                theme_read(current_theme, NULL, default_theme);
+                theme_read(current_theme, NULL);
                g_free(fname);
        }
 
@@ -1309,25 +1333,41 @@ static void themes_read(void)
        }
 }
 
+static THEME_REC *read_internal_theme(void)
+{
+       CONFIG_REC *config;
+       THEME_REC *theme;
+
+       theme = theme_create("internal", "_internal");
+
+       config = config_open(NULL, -1);
+       config_parse_data(config, default_theme, "internal");
+       theme_read_abstracts(config, theme);
+       config_close(config);
+
+       return theme;
+}
+
 void themes_init(void)
 {
        settings_add_str("lookandfeel", "theme", "default");
 
        default_formats = g_hash_table_new((GHashFunc) g_str_hash,
                                           (GCompareFunc) g_str_equal);
+       internal_theme = read_internal_theme();
 
         init_finished = FALSE;
         init_errors = NULL;
 
        themes = NULL;
-       themes_read();
+       themes_reload();
 
        command_bind("format", NULL, (SIGNAL_FUNC) cmd_format);
        command_bind("save", NULL, (SIGNAL_FUNC) cmd_save);
        signal_add("complete command format", (SIGNAL_FUNC) sig_complete_format);
        signal_add("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
-       signal_add("setup reread", (SIGNAL_FUNC) themes_read);
+       signal_add("setup reread", (SIGNAL_FUNC) themes_reload);
 
        command_set_options("format", "delete reset");
        command_set_options("save", "formats");
@@ -1337,6 +1377,7 @@ void themes_deinit(void)
 {
        while (themes != NULL)
                theme_destroy(themes->data);
+       theme_destroy(internal_theme);
 
        g_hash_table_destroy(default_formats);
        default_formats = NULL;
@@ -1346,5 +1387,5 @@ void themes_deinit(void)
        signal_remove("complete command format", (SIGNAL_FUNC) sig_complete_format);
        signal_remove("irssi init finished", (SIGNAL_FUNC) sig_print_errors);
         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
-        signal_remove("setup reread", (SIGNAL_FUNC) themes_read);
+        signal_remove("setup reread", (SIGNAL_FUNC) themes_reload);
 }
index 0a809b950acc3c27bfa07c9865b00ad7d48c3690..dcdea0fc8df97a17e336a2def1720784fe9c679d 100644 (file)
@@ -48,6 +48,8 @@ THEME_REC *theme_load(const char *name);
 void theme_register_module(const char *module, FORMAT_REC *formats);
 void theme_unregister_module(const char *module);
 
+void theme_set_default_abstract(const char *key, const char *value);
+
 #define EXPAND_FLAG_IGNORE_REPLACES     0x01 /* don't use the character replaces when expanding */
 #define EXPAND_FLAG_IGNORE_EMPTY        0x02 /* if abstract's argument is empty, or the argument is a $variable that is empty, don't try to expand it (ie. {xx }, but not {xx}) */
 #define EXPAND_FLAG_RECURSIVE_MASK      0x0f
@@ -61,6 +63,8 @@ char *theme_format_expand_data(THEME_REC *theme, const char **format,
                               char *save_last_fg, char *save_last_bg,
                               int flags);
 
+void themes_reload(void);
+
 void themes_init(void);
 void themes_deinit(void);
 
similarity index 92%
rename from apps/irssi/src/fe-text/utf8.c
rename to apps/irssi/src/fe-common/core/utf8.c
index 4049991db524c3f5d4d464382d97b3369c6cb1d2..eaa77674e5527c53a9ad0cf3b99fcd2a0ba01296 100644 (file)
       (Result) |= ((Chars)[(Count)] & 0x3f);                                 \
     }
 
-unichar get_utf8_char(const unsigned char **ptr, int len)
+int get_utf8_char(const unsigned char **ptr, int len, unichar *chr_r)
 {
        int i, result, mask, chrlen;
 
         mask = 0;
        UTF8_COMPUTE(**ptr, mask, chrlen);
        if (chrlen == -1)
-               return (unichar) -2;
+               return -2;
 
        if (chrlen > len)
-                return (unichar) -1;
+                return -1;
 
        UTF8_GET(result, *ptr, i, mask, chrlen);
        if (result == -1)
-                return (unichar) -2;
-
+                return -2;
+       
+       *chr_r = (unichar) result;
        *ptr += chrlen-1;
         return result;
 }
@@ -100,9 +101,10 @@ int strlen_utf8(const char *str)
 {
        const unsigned char *p = (const unsigned char *) str;
         int len;
+       unichar chr_r;
 
        len = 0;
-       while (*p != '\0' && get_utf8_char(&p, 6) > 0) {
+       while (*p != '\0' && get_utf8_char(&p, 6, &chr_r) > 0) {
                len++;
                 p++;
        }
@@ -180,6 +182,24 @@ void utf16_to_utf8(const unichar *str, char *out)
        *out = '\0';
 }
 
+void utf16_to_utf8_with_pos(const unichar *str, int spos, char *out, int *opos)
+{
+       int len;
+       const unichar *sstart = str;
+       char *ostart = out;
+
+       *opos = 0;
+       while (*str != '\0') {
+               len = utf16_char_to_utf8(*str, out);
+                out += len;
+
+               str++;
+               if(str - sstart == spos)
+                       *opos = out - ostart;
+       }
+       *out = '\0';
+}
+
 static const unichar wcc[] = {
        0x0, 0x300, 0x34F, 0x360, 0x363, 0x483, 0x487, 0x488, 0x48A, 0x591,
        0x5A2, 0x5A3, 0x5BA, 0x5BB, 0x5BE, 0x5BF, 0x5C0, 0x5C1, 0x5C3, 0x5C4,
diff --git a/apps/irssi/src/fe-common/core/utf8.h b/apps/irssi/src/fe-common/core/utf8.h
new file mode 100644 (file)
index 0000000..f9e3e59
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef __UTF8_H
+#define __UTF8_H
+
+/* Returns -2 = invalid, -1 = need more data, otherwise unichar. */
+int get_utf8_char(const unsigned char **ptr, int len, unichar *chr_r);
+
+/* Returns length of UTF8 string */
+int strlen_utf8(const char *str);
+
+/* UTF-8 -> unichar string. The NUL is copied as well. */
+void utf8_to_utf16(const char *str, unichar *out);
+
+/* unichar -> UTF-8 string. outbuf must be at least 6 chars long.
+   Returns outbuf string length. */
+int utf16_char_to_utf8(unichar c, char *outbuf);
+
+/* unichar -> UTF-8 string. The NUL is copied as well.
+   Make sure out is at least 6 x length of str. */
+void utf16_to_utf8(const unichar *str, char *out);
+
+/* unichar -> UTF-8 string with position transformed. The NUL is copied as well.
+   Make sure out is at least 6 x length of str. */
+void utf16_to_utf8_with_pos(const unichar *str, int spos, char *out, int *opos);
+
+/* XXX I didn't check the encoding range of big5+. This is standard big5. */
+#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
+#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
+#define is_big5_lo(lo) ((is_big5_los(lo) || is_big5_lox(lo)))
+#define is_big5_hi(hi)  (0x81 <= (hi) && (hi) <= 0xFE)
+#define is_big5(hi,lo) (is_big5_hi(hi) && is_big5_lo(lo))
+
+/* Returns width for character (0-2). */
+int utf8_width(unichar c);
+
+#endif
index 7238e50b263225863ec7c77917337b3b0dc41739..eab2c02f585656d69399e6f498f460e47e386c98 100644 (file)
@@ -76,6 +76,7 @@ void window_item_activity(WI_ITEM_REC *item, int data_level,
 static void sig_hilight_text(TEXT_DEST_REC *dest, const char *msg)
 {
        WI_ITEM_REC *item;
+        char *tagtarget;
        int data_level;
 
        if (dest->window == active_win || (dest->level & hide_level))
@@ -88,9 +89,20 @@ static void sig_hilight_text(TEXT_DEST_REC *dest, const char *msg)
                        DATA_LEVEL_MSG : DATA_LEVEL_TEXT;
        }
 
-       if ((dest->level & MSGLEVEL_HILIGHT) == 0 &&
-           hide_target_activity(data_level, dest->target))
-               return;
+       if ((dest->level & MSGLEVEL_HILIGHT) == 0 && dest->target != NULL) {
+               /* check for both target and tag/target */
+               if (hide_target_activity(data_level, dest->target))
+                       return;
+
+               tagtarget = dest->server_tag == NULL ? NULL :
+                       g_strdup_printf("%s/%s", dest->server_tag,
+                                       dest->target);
+               if (hide_target_activity(data_level, tagtarget)) {
+                       g_free(tagtarget);
+                       return;
+               }
+               g_free(tagtarget);
+       }
 
        if (dest->target != NULL) {
                item = window_item_find(dest->server, dest->target);
@@ -127,18 +139,18 @@ static void read_settings(void)
                g_strsplit(targets, " ", -1);
 
        hide_level = MSGLEVEL_NEVER | MSGLEVEL_NO_ACT |
-               level2bits(settings_get_str("activity_hide_level"));
-       msg_level = level2bits(settings_get_str("activity_msg_level"));
+               settings_get_level("activity_hide_level");
+       msg_level = settings_get_level("activity_msg_level");
        hilight_level = MSGLEVEL_HILIGHT |
-               level2bits(settings_get_str("activity_hilight_level"));
+               settings_get_level("activity_hilight_level");
 }
 
 void window_activity_init(void)
 {
        settings_add_str("lookandfeel", "activity_hide_targets", "");
-       settings_add_str("lookandfeel", "activity_hide_level", "");
-       settings_add_str("lookandfeel", "activity_msg_level", "PUBLIC");
-       settings_add_str("lookandfeel", "activity_hilight_level", "MSGS DCCMSGS");
+       settings_add_level("lookandfeel", "activity_hide_level", "");
+       settings_add_level("lookandfeel", "activity_msg_level", "PUBLIC");
+       settings_add_level("lookandfeel", "activity_hilight_level", "MSGS DCCMSGS");
 
        read_settings();
        signal_add("print text", (SIGNAL_FUNC) sig_hilight_text);
index fbfacd8540964dd4e1074b44eb9fc4d2220f1820..05332cad4bef1b0f82acc3db7285e9bb712c1a17 100644 (file)
@@ -183,7 +183,7 @@ static void cmd_window_new(const char *data, void *server, WI_ITEM_REC *item)
        window_change_server(window, server);
 }
 
-/* SYNTAX: WINDOW CLOSE [<first> [<last>] */
+/* SYNTAX: WINDOW CLOSE [<first> [<last>]] */
 static void cmd_window_close(const char *data)
 {
         GSList *tmp, *destroys;
@@ -462,17 +462,24 @@ static void cmd_window_item_goto(const char *data, SERVER_REC *server)
 {
        WI_ITEM_REC *item;
        GSList *tmp;
+       void *free_arg;
+       char *target;
+       
+       if (!cmd_get_params(data, &free_arg, 1, &target))
+               return;
 
-       if (is_numeric(data, '\0')) {
+       if (is_numeric(target, '\0')) {
                /* change to specified number */
-               tmp = g_slist_nth(active_win->items, atoi(data)-1);
+               tmp = g_slist_nth(active_win->items, atoi(target)-1);
                item = tmp == NULL ? NULL : tmp->data;
        } else {
-               item = window_item_find_window(active_win, server, data);
+               item = window_item_find_window(active_win, server, target);
        }
 
         if (item != NULL)
                 window_item_set_active(active_win, item);
+
+       cmd_params_free(free_arg);
 }
 
 /* SYNTAX: WINDOW ITEM MOVE <number>|<name> */
index 9429c00ae6236d78e3a116f621e0b915bdcb8c35..223b65244c7dcbfc6695405744a204baa0a3f116 100644 (file)
@@ -61,12 +61,25 @@ static void sig_layout_restore_item(WINDOW_REC *window, const char *type,
                WINDOW_BIND_REC *rec = window_bind_add(window, tag, name);
                 rec->sticky = TRUE;
        } else if (g_strcasecmp(type, "QUERY") == 0 && chat_type != NULL) {
+               CHAT_PROTOCOL_REC *protocol;
                /* create query immediately */
                signal_add("query created",
                           (SIGNAL_FUNC) signal_query_created_curwin);
 
                 restore_win = window;
-               chat_protocol_find(chat_type)->query_create(tag, name, TRUE);
+               
+               protocol = chat_protocol_find(chat_type);
+               if (protocol->query_create != NULL)
+                       protocol->query_create(tag, name, TRUE);
+               else {
+                       QUERY_REC *query;
+
+                       query = g_new0(QUERY_REC, 1);
+                       query->chat_type = chat_protocol_lookup(chat_type);
+                       query->name = g_strdup(name);
+                       query->server_tag = g_strdup(tag);
+                       query_init(query, TRUE);
+               }
 
                signal_remove("query created",
                              (SIGNAL_FUNC) signal_query_created_curwin);
index 2b248696ef2e148489214b41310b217513e9e01a..a03b15d79872900c50b9574bc1c7135eff1dfa7c 100644 (file)
@@ -37,6 +37,11 @@ terminfo_sources = \
 curses_sources = \
        term-curses.c
 
+cuix_sources = \
+       cuix-api.c \
+       cuix-lib.c \
+       cuix.c
+
 if NEED_TPARM
 use_tparm_sources = $(tparm_sources)
 endif
@@ -47,6 +52,12 @@ else
 use_term_sources = $(terminfo_sources)
 endif
 
+if USE_CUIX
+use_term_sources += $(cuix_sources)
+silc_LDADD += -lform -lpanel -lmenu
+INCLUDES += -I$(top_srcdir)/src/irc/core
+endif
+
 silc_SOURCES = \
         gui-entry.c \
        gui-expandos.c \
@@ -68,7 +79,6 @@ silc_SOURCES = \
         textbuffer-commands.c \
         textbuffer-reformat.c \
         textbuffer-view.c \
-       utf8.c \
         silc.c \
        module-formats.c
 
@@ -85,7 +95,6 @@ noinst_HEADERS = \
         textbuffer.h \
         textbuffer-view.h \
         textbuffer-reformat.h \
-       utf8.h \
        module.h \
        module-formats.h
 
diff --git a/apps/irssi/src/fe-text/cuix-api.c b/apps/irssi/src/fe-text/cuix-api.c
new file mode 100644 (file)
index 0000000..f8e9f61
--- /dev/null
@@ -0,0 +1,386 @@
+#include "module.h"
+#include "settings.h"
+#include "term.h"
+#include "gui-windows.h"
+#include <stdarg.h>
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+#  include <ncurses.h>
+#else
+#  include <curses.h>
+#endif
+#include <form.h>
+#include <panel.h>
+#include <menu.h>
+
+
+#include "cuix-api.h"
+
+#define INIT_ENTRIES 8
+#define X0_OFFSET 4
+#define Y0_OFFSET 2
+#define Y_OFFSET 1
+#define CUIX_FIELD_WIDTH 16
+
+object *create_object (char *title, int type, void **entries)
+{
+    object *obj;
+    void **new_entries;
+    int i;
+
+    obj = g_malloc (sizeof(object));
+    if (!obj) {
+        return NULL;
+    }
+    obj->type = type;
+    obj->title = title;
+    if (!entries) {
+        new_entries = g_new0 (void *, INIT_ENTRIES);
+        obj->entries = new_entries;
+        obj->alloced = INIT_ENTRIES;
+        obj->last = 0;
+    } else {
+        for (i = 0; ((entry **)entries)[i]; i++);
+        obj->alloced = i;
+        obj->last = i;
+        obj->entries = entries;
+    }
+    return obj;
+}
+
+
+object *create_menu (char *title)
+{
+    return create_object (title, CUIX_MENU, NULL);
+}
+
+
+object *create_form (char *title)
+{
+    return create_object (title, CUIX_FORM, NULL);
+}
+
+
+/* entries must be NULL terminated */
+object *create_list (char *title, entry **entries)
+{
+    return create_object (title, CUIX_LIST, (void **)entries);
+}
+
+entry *create_entry (char *label, int type, action_fn_type action)
+{
+    entry *entry;
+    
+    entry = g_malloc (sizeof(object));
+    if (!entry) {
+        return NULL;
+    }
+    entry->type = type;
+    entry->data = label;
+    entry->action = action;
+    return entry;
+}
+
+entry *create_menuentry (char *label, action_fn_type action)
+{
+    return create_entry (label, CUIX_MENUENTRY, action);
+}
+
+entry *create_label (char *label)
+{
+    return create_entry (label, CUIX_LABEL, NULL);
+}
+
+
+entry *create_field (char *label, action_fn_type action)
+{
+    return create_entry (label, CUIX_FIELD, action);
+}
+
+
+
+/* Adds child at the last position of father->entries */
+void attach_entry (object *father, void *child)
+{
+    void **entries;
+    int i;
+
+    /* Check that we have enough space in father->entries, otherwise alloc
+     * twice more than previously */
+    if (father->last >= father->alloced) {
+        entries = g_new0 (void *,2 * father->alloced);
+        if (!entries) {
+            fprintf (stderr, "Problem with memory allocation, quitting now...\n");
+            exit (1);
+        }
+        for (i = 0; i < father->alloced; i++) {
+            entries[i] = father->entries[i];
+        }
+        g_free (father->entries);
+        father->entries = entries;
+        father->alloced *= 2;
+    }
+    father->entries[father->last++] = child;
+}
+
+
+/* Adds a submenu to father */
+void attach_submenu (object *father, object *child)
+{ 
+
+    /* Check that both are really menus */
+    if (father->type != CUIX_MENU || child->type != CUIX_MENU) {
+        fprintf (stderr, "Typing error, trying to add %p (%d) as child of"
+                "%p (%d)\n", father, father->type, child, child->type);
+        exit (1);
+    }
+    attach_entry (father, (void *)child);
+}
+
+
+/* Returns the maximum width occupied by labels */
+int get_labels_width (object *obj)
+{
+    int i;
+    unsigned int w = 0;
+    entry *e;
+    object *o;
+
+    for (i = 0; i < obj->last; i++) {
+        e = (entry *)obj->entries[i];
+        if (e->type == CUIX_LABEL || e->type == CUIX_MENUENTRY) {
+            w = (w > strlen (e->data)) ? w : strlen (e->data);
+        }
+        if (e->type == CUIX_MENU) {
+            o = (object *)obj->entries[i];
+            w = (w > strlen (o->title)) ? w : strlen (o->title);
+        }
+
+    }
+    w += 2 * X0_OFFSET;
+    return (int)w;
+}
+
+
+/* Puts in x and y the coordinates to center an object of size objw and objh
+ * in the window win */
+void get_center (WINDOW *win, int objh, int objw, int *y, int *x)
+{
+    int begx, begy, maxx, maxy, w, h;
+    getbegyx (win, begy, begx);
+    getmaxyx (win, maxy, maxx);
+    w = maxx - begx;
+    h = maxy - begy;
+    *x = (w - objw) / 2 + begx;
+    *y = (h - objh) / 2 + begy;
+    if (*x < 0)
+        *x = 0;
+    if (*y < 0)
+        *y = 0;
+}
+
+
+
+void display_object (object *obj)
+{
+    WINDOW *subwin;
+    FORM *form;
+    MENU *menu;
+    FIELD **fields;
+    ITEM **items, *cur_item;
+    object *o;
+    entry *e;
+    char *result;
+    int i, x, y, w, h;
+    int ch;
+    p_main = new_panel(root_window->win);
+
+    if (obj->type >= CUIX_LABEL) {
+        fprintf (stderr, "Trying to display an entry %p (%d), terminating...\n",
+                obj, obj->type);
+        exit (1);
+    }
+
+    switch (obj->type) {
+        case CUIX_LIST:
+            w = get_labels_width (obj);
+            h = Y_OFFSET * obj->last + 2 * Y0_OFFSET;
+            get_center (root_window->win, h, w, &y, &x);
+            cuix_win = newwin (h, w, y, x);
+            box (cuix_win, 0, 0);
+            p_cuix = new_panel(cuix_win);
+            x = X0_OFFSET;
+            y = Y0_OFFSET;
+
+            for (i = 0; i < obj->last; i++) {
+                e = (entry *)obj->entries[i];
+                if (e->type != CUIX_LABEL) {
+                    fprintf (stderr, "Non-label entry in a list.\n");
+                    exit (1);
+                } 
+                wmove (cuix_win,y,x);
+                waddstr (cuix_win,e->data);
+                y += Y_OFFSET;
+                x = X0_OFFSET;
+            }
+            top_panel (p_cuix);
+            update_panels();
+            doupdate();
+            wgetch(cuix_win);
+            /* refresh (); */
+            /* wrefresh (cuix_win); */
+            break;
+
+        case CUIX_FORM:
+            w = get_labels_width (obj);
+            w = (w > CUIX_FIELD_WIDTH + 2 * X0_OFFSET) ?
+                w : CUIX_FIELD_WIDTH + 2 * X0_OFFSET;
+            h = Y_OFFSET * obj->last + 2 * Y0_OFFSET;
+            fields = g_new0 (FIELD *, obj->last + 1);
+            for (i = 0; i < obj->last; i++) {
+                e = (entry *)obj->entries[i];
+                fields[i] = new_field (1, w,
+                        Y0_OFFSET + i * Y_OFFSET, X0_OFFSET, 0, 0);
+                if (e->type == CUIX_LABEL) {
+                    field_opts_off (fields[i], O_ACTIVE);
+                    field_opts_off (fields[i], O_EDIT);
+                    set_field_back  (fields[i], A_BOLD);
+                }
+                set_field_buffer (fields[i], 0, e->data);
+            }
+            fields[obj->last] = NULL;
+            form = new_form (fields);
+            scale_form (form, &h, &w);
+            h += Y0_OFFSET;
+            w += 2 * X0_OFFSET;
+            get_center (root_window->win, h, w, &y, &x);
+            cuix_win = newwin (h, w, y, x);
+            keypad (cuix_win, TRUE);
+            nonl ();
+            set_form_win (form, cuix_win);
+            set_form_sub (form, derwin(cuix_win, w, h, X0_OFFSET, Y0_OFFSET));
+            post_form (form);
+            box (cuix_win, 0, 0);
+            p_cuix = new_panel (cuix_win);
+            top_panel (p_cuix);
+            while((ch = wgetch(cuix_win)) != '\n' && ch != '\r' && ch != 27 /* ESC */) {       
+                switch(ch) {       
+                    case KEY_DOWN:
+                        /* Go to next field */
+                        form_driver(form, REQ_NEXT_FIELD);
+                        /* Go to the end of the present buffer */
+                        /* Leaves nicely at the last character */
+                        form_driver(form, REQ_END_LINE);
+                        break;
+                    case KEY_UP:
+                        /* Go to previous field */
+                        form_driver(form, REQ_PREV_FIELD);
+                        form_driver(form, REQ_END_LINE);
+                        break;
+                    case KEY_BACKSPACE:
+                        form_driver(form, REQ_PREV_CHAR);
+                        form_driver(form, REQ_DEL_CHAR);
+                        break;
+                    case KEY_LEFT:
+                        form_driver(form, REQ_PREV_CHAR);
+                        break;
+                    case KEY_RIGHT:
+                        form_driver(form, REQ_NEXT_CHAR);
+                        break;
+                    default:
+                        /* If this is a normal character, it gets */
+                        /* Printed                                */    
+                        form_driver(form, ch);
+                        break;
+                }
+            }
+            form_driver (form, REQ_VALIDATION);
+            if (ch != 27) {
+                for (i = 0; i < obj->last; i++) {
+                    e = (entry *)obj->entries[i];
+                    if (e->type == CUIX_FIELD) {
+                        result = field_buffer(fields[i],0);
+                        e->action (result);
+                    }
+                }
+            }
+            for (i = 0; i < obj->last; i++) {
+                free_field (fields[i]);
+            }
+            g_free (fields);
+            unpost_form (form);
+
+            break;
+
+        case CUIX_MENU:
+            w = get_labels_width (obj);
+            w = (w > CUIX_FIELD_WIDTH + 2 * X0_OFFSET) ?
+                w : CUIX_FIELD_WIDTH + 2 * X0_OFFSET;
+            h = Y_OFFSET * obj->last + 2 * Y0_OFFSET;
+            items = g_new0 (ITEM *, obj->last + 1);
+            for (i = 0; i < obj->last; i++) {
+                e = (entry *)obj->entries[i];
+                o = (object *)obj->entries[i];
+                if (e->type == CUIX_MENUENTRY) {
+                    items[i] = new_item (e->data, "");
+                    set_item_userptr (items[i], (void*)e);
+                } else {
+                    if (e->type == CUIX_LABEL) {
+                        items[i] = new_item (e->data, "");
+                        item_opts_off (items[i], O_SELECTABLE);
+                    } else {
+                        items[i] = new_item (o->title, " (SUB) ");
+                        set_item_userptr (items[i], (void*)o);
+                    }
+                }
+            }
+            items[obj->last] = NULL;
+            menu = new_menu (items);
+            set_menu_mark (menu, " * ");
+            scale_menu (menu, &h, &w);
+            h += 4 * Y0_OFFSET;
+            w += 4 * X0_OFFSET;
+            get_center (root_window->win, h, w, &y, &x);
+             cuix_win = newwin (h, w, y, x);
+            keypad (cuix_win, TRUE);
+            nonl ();
+            set_menu_win (menu, cuix_win);
+            subwin = derwin (cuix_win,
+                    h - 2 * Y0_OFFSET, w - 2 * X0_OFFSET, Y0_OFFSET, X0_OFFSET);
+            set_menu_sub (menu, subwin);
+            box (cuix_win, 0, 0);
+            post_menu (menu);
+            p_cuix = new_panel (cuix_win);
+            top_panel (p_cuix);
+            while((ch = wgetch(cuix_win)) != 27 /* ESC */) {       
+                switch(ch) {       
+                    case KEY_DOWN:
+                        menu_driver(menu, REQ_DOWN_ITEM);
+                        break;
+                    case KEY_UP:
+                        menu_driver(menu, REQ_UP_ITEM);
+                        break;
+                    case '\n':
+                    case '\r':
+                        cur_item = current_item(menu);
+                        e = (entry *)item_userptr(cur_item);
+                        o = (object *)item_userptr(cur_item);
+                        if (e->type == CUIX_MENUENTRY)
+                        {
+                            e->action ("");
+                        } else {
+                            display_object (o);
+                        }
+                        goto end;
+                        break;
+                    default:
+                        break;
+                }
+            }
+end:
+            for (i = 0; i < obj->last; i++) {
+                free_item (items[i]);
+            }
+            g_free (items);
+            unpost_menu (menu);
+    }
+}
diff --git a/apps/irssi/src/fe-text/cuix-api.h b/apps/irssi/src/fe-text/cuix-api.h
new file mode 100644 (file)
index 0000000..9dbb7bc
--- /dev/null
@@ -0,0 +1,68 @@
+#ifndef __CUIX_API_H
+#define __CUIX_API_H
+
+#include "term-curses.h"
+
+#define MAX_FIELD_SIZE 64
+
+WINDOW *cuix_win;
+PANEL *p_main;
+PANEL *p_cuix;
+
+enum objtype {
+    /* For objects */
+    CUIX_MENU,
+    CUIX_FORM,
+    CUIX_LIST,
+    /* For entries */
+    /* NB: LABEL must stay the first entry, as it is used to test if we have
+     * an object or an entry */
+    CUIX_LABEL,
+    CUIX_FIELD,
+    CUIX_MENUENTRY
+};
+
+
+
+/* This is the type of the action to be executed when the entry has been
+ * successfully selected (in case of a menuentry) or filled (in case of a
+ * field). */
+typedef int(*action_fn_type)(char *);
+
+
+typedef struct entry {
+    int type;
+    char *data; /* contains label or submenu title */
+    action_fn_type action;
+} entry;
+
+
+typedef struct object {
+    int type;
+    char *title;
+    void **entries;
+    int alloced; /* defines the current size of entries */ 
+    int last; /* index of the first non-alloced entry */
+} object;
+
+
+/* Object definitions */
+
+object *create_menu (char *title);
+object *create_form (char *title);
+/* entries must be NULL terminated */
+object *create_list (char *title, entry **entries);
+
+
+entry *create_menuentry (char *label, action_fn_type action);
+entry *create_label (char *label);
+entry *create_field (char *label, action_fn_type action);
+
+void attach_submenu (object *father, object *child);
+void attach_entry (object *father, void *child);
+
+void display_object (object *obj);
+
+void my_menu(void);
+
+#endif /* __CUIX_API_H */
diff --git a/apps/irssi/src/fe-text/cuix-lib.c b/apps/irssi/src/fe-text/cuix-lib.c
new file mode 100644 (file)
index 0000000..657e657
--- /dev/null
@@ -0,0 +1,139 @@
+#include "module.h"
+#include "settings.h"
+#include "cuix-lib.h"
+#include "signals.h"
+#include "irc.h"
+#include "irc-channels.h"
+#include "mode-lists.h"
+#include "gui-windows.h"
+
+
+int do_nothing (char *foo)
+{
+    (void)foo;
+    return 0;
+}
+
+
+void display_message (char *message)
+{
+    object *list;
+    entry *text, *entries[2];
+
+    text = create_label (message);
+    entries[0] = text;
+    entries[1] = NULL;
+    list = create_list ("Message", entries);
+    display_object (list);
+}
+
+
+int change_nick (char *nick)
+{
+    SERVER_REC *server;
+    WI_ITEM_REC *wiitem;
+    if (active_win == NULL) {
+        server = NULL;
+        wiitem = NULL;
+    } else {
+        server = active_win->active_server != NULL ?
+            active_win->active_server : active_win->connect_server;
+        wiitem = active_win->active;
+    } 
+    signal_emit("command nick", 3, nick, server, wiitem);
+    return 0;
+}
+
+
+
+int show_banlist (char *nothing)
+{
+    GSList *tmp;
+    IRC_CHANNEL_REC *chan = IRC_CHANNEL(active_win->active);
+    BAN_REC *ban;
+    object *list;
+    entry *entry, **entries;
+    unsigned int size, i;
+    GString **baninfo;
+
+    if (!chan) {
+        display_message ("This is not a channel");
+        return 1;
+    }
+    if (!chan->banlist) {
+        display_message ("No bans set");
+        return 0;
+    }
+
+    size = (unsigned int) g_slist_length (chan->banlist);
+    entries = g_new0 (struct entry *, size + 1);
+    baninfo = g_new0 (GString *, size);
+
+    for (tmp = chan->banlist, i = 0; tmp; tmp = tmp->next, i++) {
+        ban = tmp->data;
+        baninfo[i] = g_string_new (NULL);
+        g_string_sprintf (baninfo[i], "%s set by %s %d seconds ago", ban->ban, ban->setby, (int)(time(NULL)-ban->time));
+        entry = create_label (baninfo[i]->str);
+        entries[i] = entry;
+    }
+
+    list = create_list ("Bans", entries);
+    display_object (list);
+    for (i = 0; i < size; i++) {
+        g_string_free (baninfo[i], FALSE);
+    }
+    g_free (entries);
+    g_free (baninfo);
+
+    return 0;
+}
+
+
+int change_nick_form (char *nothing) {
+    object *form;
+    entry *question, *answer;
+    (void)nothing;
+
+    form = create_form ("True!");
+    question = create_label ("Enter your new nick");
+    answer = create_field ("", change_nick);
+    attach_entry (form, question);
+    attach_entry (form, answer);
+    display_object (form);
+    return 0;
+}
+
+
+int about_list (char *nothing) 
+{
+    (void)nothing;
+
+    display_message ("(c) irssi; See http://www.irssi.org.");
+    return 0;
+}
+
+
+
+
+int home_menu (char *nothing) 
+{
+    /* Objects declaration */
+    object *root_menu;
+    entry *about, *banlist, *nick;
+    (void)nothing;
+
+    /* Objects initialisation */
+    root_menu = create_menu ("My root menu");
+    banlist = create_menuentry ("Banlist", show_banlist);
+    nick = create_menuentry ("Change nick", change_nick_form);
+    about = create_menuentry ("About", about_list);
+
+    /* Layout */
+    attach_entry (root_menu, (void *)banlist);
+    attach_entry (root_menu, (void *)nick);
+    attach_entry (root_menu, (void *)about);
+
+    /* Declare that the object is ready to be displayed and do it */
+    display_object (root_menu);
+    return 0;
+}
diff --git a/apps/irssi/src/fe-text/cuix-lib.h b/apps/irssi/src/fe-text/cuix-lib.h
new file mode 100644 (file)
index 0000000..1839a5a
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef __CUIX_LIB_H
+#define __CUIX_LIB_H
+
+#include "cuix-api.h"
+
+int home_menu (char *);
+
+
+#endif /* __CUIX_LIB_H */
diff --git a/apps/irssi/src/fe-text/cuix.c b/apps/irssi/src/fe-text/cuix.c
new file mode 100644 (file)
index 0000000..ef95470
--- /dev/null
@@ -0,0 +1,39 @@
+#include "module.h"
+#include "settings.h"
+#include "cuix-api.h"
+#include "cuix-lib.h"
+#include "cuix.h"
+#include "term.h"
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+#  include <ncurses.h>
+#else
+#  include <curses.h>
+#endif
+
+
+void cuix_destroy (void)
+{
+    if (cuix_win) {
+        del_panel (p_cuix);
+        delwin(cuix_win);
+    }
+    cuix_win = NULL;
+    cuix_active = 0;
+    update_panels ();
+    doupdate();
+    term_refresh (root_window);
+    irssi_redraw ();
+}
+
+void cuix_create(void)
+{
+    home_menu (NULL);
+    cuix_destroy ();
+}
+
+void cuix_refresh (void)
+{
+    update_panels ();
+}
+
+
diff --git a/apps/irssi/src/fe-text/cuix.h b/apps/irssi/src/fe-text/cuix.h
new file mode 100644 (file)
index 0000000..ca8ffce
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __CUIX_TRY_H
+#define __CUIX_TRY_H
+
+int cuix_active;
+
+void cuix_create (void);
+void cuix_destroy (void);
+void cuix_refresh (void);
+
+
+#endif /* __CUIX_TRY_H */
index f6bfb27dc8c7dae032fa4c785012e6efa3a938f6..7dcec35801a35b7c8631f240822d70e57aa35a26 100644 (file)
@@ -68,21 +68,131 @@ void gui_entry_destroy(GUI_ENTRY_REC *entry)
         g_free(entry);
 }
 
+/* big5 functions */
+#define big5_width(ch) ((ch)>0xff ? 2:1)
+
+void unichars_to_big5(const unichar *str, char *out)
+{
+       for (; *str != '\0'; str++) {
+               if (*str > 0xff)
+                       *out++ = (*str >> 8) & 0xff;
+               *out++ = *str & 0xff;
+       }
+       *out = '\0';
+}
+
+int strlen_big5(const unsigned char *str)
+{
+       int len=0;
+
+       if (term_type != TERM_TYPE_BIG5)
+               return strlen(str);
+
+       while (*str != '\0') {
+               if (is_big5(str[0], str[1]))
+                       str++;
+               len++;
+               str++;
+       }
+       return len;
+}
+
+void unichars_to_big5_with_pos(const unichar *str, int spos, char *out, int *opos)
+{
+       const unichar *sstart = str;
+       char *ostart = out;
+
+       *opos = 0;
+       while(*str != '\0')
+       {
+               if(*str > 0xff)
+                       *out ++ = (*str >> 8) & 0xff;
+               *out ++ = *str & 0xff;
+               str ++;
+               if(str - sstart == spos)
+                       *opos = out - ostart;
+       }
+       *out = '\0';
+}
+
+void big5_to_unichars(const char *str, unichar *out)
+{
+       const unsigned char *p = (const unsigned char *) str;
+
+       while (*p != '\0') {
+               if (is_big5(p[0], p[1])) {
+                       *out++ = p[0] << 8 | p[1];
+                       p += 2;
+               } else {
+                       *out++ = *p++;
+               }
+       }
+       *out = '\0';
+}
+
+/* ----------------------------- */
+
+static int pos2scrpos(GUI_ENTRY_REC *entry, int pos)
+{
+       unichar *p;
+       int xpos = 0;
+
+       for (p = entry->text; p - entry->text < pos; p++) {
+               if (term_type == TERM_TYPE_BIG5)
+                       xpos += big5_width(*p);
+               else if (entry->utf8)
+                       xpos += utf8_width(*p);
+               else
+                       xpos++;
+       }
+       return xpos;
+}
+
+static int scrpos2pos(GUI_ENTRY_REC *entry, int pos)
+{
+       int i, width, xpos;
+
+       for (i = 0, xpos = 0; entry->text[i]; i++) {
+               unichar *p = entry->text+i;
+
+               if (term_type == TERM_TYPE_BIG5)
+                       width = big5_width(*p);
+               else if (entry->utf8)
+                       width = utf8_width(*p);
+               else
+                       width = 1;
+
+               if (xpos + width > pos)
+                       break;
+               xpos += width;
+       }
+
+       if (xpos == pos)
+               return i;
+       else
+               return i-1;
+}
+
 /* Fixes the cursor position in screen */
 static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry)
 {
        int old_scrstart;
 
-        old_scrstart = entry->scrstart;
-       if (entry->pos - entry->scrstart < entry->width-2 - entry->promptlen &&
-           entry->pos - entry->scrstart > 0) {
-               entry->scrpos = entry->pos - entry->scrstart;
-       } else if (entry->pos < entry->width-1 - entry->promptlen) {
+       /* assume prompt len == prompt scrlen */
+       int start = pos2scrpos(entry, entry->scrstart);
+       int now = pos2scrpos(entry, entry->pos);
+
+       old_scrstart = entry->scrstart;
+       if (now-start < entry->width - 2 - entry->promptlen && now-start > 0)
+               entry->scrpos = now-start;
+       else if (now < entry->width - 1 - entry->promptlen) {
                entry->scrstart = 0;
-               entry->scrpos = entry->pos;
+               entry->scrpos = now;
        } else {
-               entry->scrpos = (entry->width - entry->promptlen)*2/3;
-               entry->scrstart = entry->pos - entry->scrpos;
+               entry->scrstart = scrpos2pos(entry, now-(entry->width -
+                                                        entry->promptlen)*2/3);
+               start = pos2scrpos(entry, entry->scrstart);
+               entry->scrpos = now - start;
        }
 
        if (old_scrstart != entry->scrstart)
@@ -94,8 +204,11 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
        const unichar *p;
        int xpos, end_xpos;
 
-        xpos = entry->xpos + entry->promptlen + pos;
+       xpos = entry->xpos + entry->promptlen + 
+               pos2scrpos(entry, pos + entry->scrstart) - 
+               pos2scrpos(entry, entry->scrstart);
         end_xpos = entry->xpos + entry->width;
+
        if (xpos > end_xpos)
                 return;
 
@@ -105,7 +218,15 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
        p = entry->scrstart + pos < entry->text_len ?
                entry->text + entry->scrstart + pos : empty_str;
        for (; *p != '\0'; p++) {
-               xpos += utf8_width(*p);
+               if (entry->hidden)
+                       xpos++;
+               else if (term_type == TERM_TYPE_BIG5)
+                       xpos += big5_width(*p);
+               else if (entry->utf8)
+                       xpos += utf8_width(*p);
+               else
+                       xpos++;
+
                if (xpos > end_xpos)
                        break;
 
@@ -121,7 +242,7 @@ static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos)
        }
 
         /* clear the rest of the input line */
-        if (end_xpos == term_width)
+        if (end_xpos == term_width-1)
                term_clrtoeol(root_window);
        else {
                while (xpos < end_xpos) {
@@ -259,8 +380,34 @@ char *gui_entry_get_text(GUI_ENTRY_REC *entry)
        if (entry->utf8)
                utf16_to_utf8(entry->text, buf);
        else {
-               for (i = 0; i <= entry->text_len; i++)
-                       buf[i] = entry->text[i];
+               if (term_type == TERM_TYPE_BIG5)
+                       unichars_to_big5(entry->text, buf);
+               else
+                       for (i = 0; i <= entry->text_len; i++)
+                               buf[i] = entry->text[i];
+       }
+       return buf;
+}
+
+char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos)
+{
+       char *buf;
+        int i;
+
+       g_return_val_if_fail(entry != NULL, NULL);
+
+       buf = g_malloc(entry->text_len*6 + 1);
+       if (entry->utf8)
+               utf16_to_utf8_with_pos(entry->text, entry->pos, buf, pos);
+       else {
+               if(term_type==TERM_TYPE_BIG5)
+                       unichars_to_big5_with_pos(entry->text, entry->pos, buf, pos);
+               else
+               {
+                       for (i = 0; i <= entry->text_len; i++)
+                               buf[i] = entry->text[i];
+                       *pos = entry->pos;
+               }
        }
        return buf;
 }
@@ -275,7 +422,7 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
 
         gui_entry_redraw_from(entry, entry->pos);
 
-       len = !entry->utf8 ? strlen(str) : strlen_utf8(str);
+       len = !entry->utf8 ? strlen_big5(str) : strlen_utf8(str);
         entry_text_grow(entry, len);
 
         /* make space for the string */
@@ -283,8 +430,14 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str)
                  (entry->text_len-entry->pos + 1) * sizeof(unichar));
 
        if (!entry->utf8) {
-               for (i = 0; i < len; i++)
-                        entry->text[entry->pos+i] = str[i];
+               if (term_type == TERM_TYPE_BIG5) {
+                       chr = entry->text[entry->pos + len];
+                       big5_to_unichars(str, entry->text + entry->pos);
+                       entry->text[entry->pos + len] = chr;
+               } else {
+                       for (i = 0; i < len; i++)
+                               entry->text[entry->pos + i] = str[i];
+               }
        } else {
                 chr = entry->text[entry->pos+len];
                utf8_to_utf16(str, entry->text+entry->pos);
@@ -334,13 +487,26 @@ char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
        buf = g_malloc(entry->cutbuffer_len*6 + 1);
        if (entry->utf8)
                utf16_to_utf8(entry->cutbuffer, buf);
-       else {
+       else if (term_type == TERM_TYPE_BIG5) {
+               unichars_to_big5(entry->cutbuffer, buf);
+       } else {
                for (i = 0; i <= entry->cutbuffer_len; i++)
                        buf[i] = entry->cutbuffer[i];
        }
        return buf;
 }
 
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
+{
+       int newpos, size = 0;
+
+       g_return_if_fail(entry != NULL);
+
+       for (newpos = gui_entry_get_pos(entry); newpos > pos; size++)
+               newpos = newpos - 1;
+       gui_entry_erase(entry, size, update_cutbuffer);
+}
+
 void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
 {
         g_return_if_fail(entry != NULL);
@@ -451,6 +617,123 @@ void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
        gui_entry_draw(entry);
 }
 
+void gui_entry_transpose_words(GUI_ENTRY_REC *entry)
+{
+       int spos1, epos1, spos2, epos2;
+
+       /* find last position */
+       epos2 = entry->pos;
+       while (epos2 < entry->text_len && !i_isalnum(entry->text[epos2]))
+               epos2++;
+       while (epos2 < entry->text_len &&  i_isalnum(entry->text[epos2]))
+               epos2++;
+
+       /* find other position */
+       spos2 = epos2;
+       while (spos2 > 0 && !i_isalnum(entry->text[spos2-1]))
+               spos2--;
+       while (spos2 > 0 &&  i_isalnum(entry->text[spos2-1]))
+               spos2--;
+
+       epos1 = spos2;
+       while (epos1 > 0 && !i_isalnum(entry->text[epos1-1]))
+               epos1--;
+
+       spos1 = epos1;
+       while (spos1 > 0 && i_isalnum(entry->text[spos1-1]))
+               spos1--;
+
+       /* do wordswap if any found */
+       if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) {
+               unichar *first, *sep, *second;
+               int i;
+
+               first  = (unichar *) g_malloc( (epos1 - spos1) * sizeof(unichar) );
+               sep    = (unichar *) g_malloc( (spos2 - epos1) * sizeof(unichar) );
+               second = (unichar *) g_malloc( (epos2 - spos2) * sizeof(unichar) );
+
+               for (i = spos1; i < epos1; i++)
+                       first[i-spos1] = entry->text[i];
+               for (i = epos1; i < spos2; i++)
+                       sep[i-epos1] = entry->text[i];
+               for (i = spos2; i < epos2; i++)
+                       second[i-spos2] = entry->text[i];
+
+               entry->pos = spos1;
+               for (i = 0; i < epos2-spos2; i++)
+                       entry->text[entry->pos++] = second[i];
+               for (i = 0; i < spos2-epos1; i++)
+                       entry->text[entry->pos++] = sep[i];
+               for (i = 0; i < epos1-spos1; i++)
+                       entry->text[entry->pos++] = first[i];
+
+               g_free(first);
+               g_free(sep);
+               g_free(second);
+
+       }
+       
+       gui_entry_redraw_from(entry, spos1);
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
+}
+
+void gui_entry_capitalize_word(GUI_ENTRY_REC *entry)
+{
+       int pos = entry->pos;
+       while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
+               pos++;
+
+       if (pos < entry->text_len) {
+               entry->text[pos] = i_toupper(entry->text[pos]);
+               pos++;
+       }
+
+       while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
+               entry->text[pos] = i_tolower(entry->text[pos]);
+               pos++;
+       }
+
+       gui_entry_redraw_from(entry, entry->pos);
+       entry->pos = pos;
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
+}
+
+void gui_entry_downcase_word(GUI_ENTRY_REC *entry)
+{
+       int pos = entry->pos;
+       while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
+               pos++;
+
+       while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
+               entry->text[pos] = i_tolower(entry->text[pos]);
+               pos++;
+       }
+
+       gui_entry_redraw_from(entry, entry->pos);
+       entry->pos = pos;
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
+}
+
+void gui_entry_upcase_word(GUI_ENTRY_REC *entry)
+{
+       int pos = entry->pos;
+       while (pos < entry->text_len && !i_isalnum(entry->text[pos]))
+               pos++;
+
+       while (pos < entry->text_len && i_isalnum(entry->text[pos])) {
+               entry->text[pos] = i_toupper(entry->text[pos]);
+               pos++;
+       }
+
+       gui_entry_redraw_from(entry, entry->pos);
+       entry->pos = pos;
+       gui_entry_fix_cursor(entry);
+       gui_entry_draw(entry);
+}
+
 int gui_entry_get_pos(GUI_ENTRY_REC *entry)
 {
         g_return_val_if_fail(entry != NULL, 0);
@@ -473,7 +756,7 @@ void gui_entry_move_pos(GUI_ENTRY_REC *entry, int pos)
 {
         g_return_if_fail(entry != NULL);
 
-       if (entry->pos+pos >= 0 && entry->pos+pos <= entry->text_len)
+       if (entry->pos + pos >= 0 && entry->pos + pos <= entry->text_len)
                entry->pos += pos;
 
        gui_entry_fix_cursor(entry);
index 2ad38041fe719d34a8d14520c3ab366ff4a532aa..e5ab672a9b2fb71a508de94e9d68be752addd856 100644 (file)
@@ -34,16 +34,23 @@ void gui_entry_set_utf8(GUI_ENTRY_REC *entry, int utf8);
 
 void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str);
 char *gui_entry_get_text(GUI_ENTRY_REC *entry);
+char *gui_entry_get_text_and_pos(GUI_ENTRY_REC *entry, int *pos);
 
 void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str);
 void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr);
 
 char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry);
+void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer);
 void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer);
 void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
 void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
 
 void gui_entry_transpose_chars(GUI_ENTRY_REC *entry);
+void gui_entry_transpose_words(GUI_ENTRY_REC *entry);
+
+void gui_entry_capitalize_word(GUI_ENTRY_REC *entry);
+void gui_entry_downcase_word(GUI_ENTRY_REC *entry);
+void gui_entry_upcase_word(GUI_ENTRY_REC *entry);
 
 int gui_entry_get_pos(GUI_ENTRY_REC *entry);
 void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos);
index 2d8a8cde009125e00615d83e77389b1b467f22e1..089ac6b1e467c6e8b3943bb44ca8b5085aa7132e 100644 (file)
 #include "term.h"
 #include "gui-printtext.h"
 #include "gui-windows.h"
+#ifdef HAVE_CUIX
+#include "cuix.h"
+#endif
 
 int mirc_colors[] = { 15, 0, 1, 2, 12, 4, 5, 6, 14, 10, 3, 11, 9, 13, 8, 7 };
-static int scrollback_lines, scrollback_hours, scrollback_burst_remove;
+static int scrollback_lines, scrollback_time, scrollback_burst_remove;
 
 static int last_fg, last_bg, last_flags;
 static int next_xpos, next_ypos;
@@ -120,7 +123,7 @@ 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;
+       old_time = time(NULL)-scrollback_time+1;
        if (view->buffer->lines_count >=
            scrollback_lines+scrollback_burst_remove) {
                 /* remove lines by line count */
@@ -130,7 +133,7 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
                            scrollback_lines == 0) {
                                /* too new line, don't remove yet - also
                                   if scrollback_lines is 0, we want to check
-                                  only scrollback_hours setting. */
+                                  only scrollback_time setting. */
                                break;
                        }
                        textbuffer_view_remove_line(view, line);
@@ -255,7 +258,7 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
                if (flags & GUI_PRINT_FLAG_CLRTOEOL)
                        term_clrtoeol(root_window);
                term_addstr(root_window, str);
-               next_xpos += strlen(str);
+               next_xpos += strlen(str); /* FIXME utf8 or big5 */
                 return;
        }
 
@@ -281,6 +284,9 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
        }
        if (gui->use_insert_after)
                 gui->insert_after = insert_after;
+#ifdef HAVE_CUIX
+        cuix_refresh ();
+#endif
 }
 
 static void sig_gui_printtext_finished(WINDOW_REC *window)
@@ -302,7 +308,7 @@ static void sig_gui_printtext_finished(WINDOW_REC *window)
 static void read_settings(void)
 {
        scrollback_lines = settings_get_int("scrollback_lines");
-       scrollback_hours = settings_get_int("scrollback_hours");
+       scrollback_time = settings_get_time("scrollback_time")/1000;
         scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
 }
 
@@ -314,7 +320,7 @@ void gui_printtext_init(void)
                                            (GCompareFunc) g_str_equal);
 
        settings_add_int("history", "scrollback_lines", 500);
-       settings_add_int("history", "scrollback_hours", 24);
+       settings_add_time("history", "scrollback_time", "1day");
        settings_add_int("history", "scrollback_burst_remove", 10);
 
        signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
index 4de1936e3adc52b853ab2dbb6498172eea1a42cb..52e100f0124c87cf43bc0ac90b9b726012e56211 100644 (file)
 */
 
 #include "module.h"
+#include "module-formats.h"
 #include "signals.h"
 #include "misc.h"
 #include "settings.h"
 #include "special-vars.h"
+#include "levels.h"
 #include "servers.h"
 
 #include "completion.h"
 #include "command-history.h"
 #include "keyboard.h"
 #include "translation.h"
+#include "printtext.h"
 
 #include "term.h"
 #include "gui-entry.h"
@@ -51,7 +54,18 @@ static ENTRY_REDIRECT_REC *redir;
 static int escape_next_key;
 
 static int readtag;
-static time_t idle_time;
+static unichar prev_key;
+static GTimeVal last_keypress;
+
+static int paste_detect_time, paste_detect_keycount, paste_verify_line_count;
+static int paste_state, paste_keycount;
+static char *paste_entry, *prev_entry;
+static int paste_entry_pos, prev_entry_pos;
+static GArray *paste_buffer;
+
+static char *paste_old_prompt;
+static int paste_prompt, paste_line_count;
+static int paste_join_multiline;
 
 static void sig_input(void);
 
@@ -113,8 +127,10 @@ static int get_scroll_count(void)
 
        str = settings_get_str("scroll_page_count");
        count = atof(str + (*str == '/'));
-       if (count <= 0)
+       if (count == 0)
                count = 1;
+       else if (count < 0)
+               count = active_mainwin->height-active_mainwin->statusbar_lines+count;
        else if (count < 1)
                 count = 1.0/count;
 
@@ -134,10 +150,308 @@ static void window_next_page(void)
        gui_window_scroll(active_win, get_scroll_count());
 }
 
+static void paste_buffer_join_lines(GArray *buf)
+{
+#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
+       unsigned int i, count, indent, line_len;
+       unichar *arr, *dest, *last_lf_pos;
+       int last_lf;
+
+       /* first check if we actually want to join anything. This is assuming
+          that we only want to join lines if
+
+          a) first line doesn't begin with whitespace
+          b) subsequent lines begin with same amount of whitespace
+          c) whenever there's no whitespace, goto a)
+
+          For example:
+
+          line 1
+            line 2
+            line 3
+          line 4
+          line 5
+            line 6
+
+          ->
+
+          line1 line2 line 3
+          line4
+          line5 line 6
+       */
+       if (buf->len == 0)
+               return;
+
+       arr = (unichar *) paste_buffer->data;
+
+       /* first line */
+       if (IS_WHITE(arr[0]))
+               return;
+
+       /* find the first beginning of indented line */
+       for (i = 1; i < buf->len; i++) {
+               if (arr[i-1] == '\n' && IS_WHITE(arr[i]))
+                       break;
+       }
+       if (i == buf->len)
+               return;
+
+       /* get how much indentation we have.. */
+       for (indent = 0; i < buf->len; i++, indent++) {
+               if (!IS_WHITE(arr[i]))
+                       break;
+       }
+       if (i == buf->len)
+               return;
+
+       /* now, enforce these to all subsequent lines */
+       count = indent; last_lf = TRUE;
+       for (; i < buf->len; i++) {
+               if (last_lf) {
+                       if (IS_WHITE(arr[i]))
+                               count++;
+                       else {
+                               last_lf = FALSE;
+                               if (count != 0 && count != indent)
+                                       return;
+                               count = 0;
+                       }
+               }
+               if (arr[i] == '\n')
+                       last_lf = TRUE;
+       }
+
+       /* all looks fine - now remove the whitespace, but don't let lines
+          get longer than 400 chars */
+       dest = arr; last_lf = TRUE; last_lf_pos = NULL; line_len = 0;
+       for (i = 0; i < buf->len; i++) {
+               if (last_lf && IS_WHITE(arr[i])) {
+                       /* whitespace, ignore */
+               } else if (arr[i] == '\n') {
+                       if (!last_lf && i+1 != buf->len &&
+                           IS_WHITE(arr[i+1])) {
+                               last_lf_pos = dest;
+                               *dest++ = ' ';
+                       } else {
+                               *dest++ = '\n'; /* double-LF */
+                               line_len = 0;
+                               last_lf_pos = NULL;
+                       }
+                       last_lf = TRUE;
+               } else {
+                       last_lf = FALSE;
+                       if (++line_len >= 400 && last_lf_pos != NULL) {
+                               memmove(last_lf_pos+1, last_lf_pos,
+                                       dest - last_lf_pos);
+                               *last_lf_pos = '\n'; last_lf_pos = NULL;
+                               line_len = 0;
+                               dest++;
+                       }
+                       *dest++ = arr[i];
+               }
+       }
+       g_array_set_size(buf, dest - arr);
+}
+
+static void paste_send(void)
+{
+       HISTORY_REC *history;
+       unichar *arr;
+       GString *str;
+       char out[10], *text;
+       unsigned int i;
+
+       if (paste_join_multiline)
+               paste_buffer_join_lines(paste_buffer);
+
+       arr = (unichar *) paste_buffer->data;
+       if (active_entry->text_len == 0)
+               i = 0;
+       else {
+               /* first line has to be kludged kind of to get pasting in the
+                  middle of line right.. */
+               for (i = 0; i < paste_buffer->len; i++) {
+                       if (arr[i] == '\r' || arr[i] == '\n') {
+                               i++;
+                               break;
+                       }
+
+                       gui_entry_insert_char(active_entry, arr[i]);
+               }
+
+               text = gui_entry_get_text(active_entry);
+               history = command_history_current(active_win);
+               command_history_add(history, text);
+
+               translate_output(text);
+               signal_emit("send command", 3, text,
+                           active_win->active_server, active_win->active);
+               g_free(text);
+       }
+
+       /* rest of the lines */
+       str = g_string_new(NULL);
+       for (; i < paste_buffer->len; i++) {
+               if (arr[i] == '\r' || arr[i] == '\n') {
+                       history = command_history_current(active_win);
+                       command_history_add(history, str->str);
+
+                       translate_output(str->str);
+                       signal_emit("send command", 3, str->str,
+                                   active_win->active_server,
+                                   active_win->active);
+                       g_string_truncate(str, 0);
+               } else if (active_entry->utf8) {
+                       out[utf16_char_to_utf8(arr[i], out)] = '\0';
+                       g_string_append(str, out);
+               } else if (term_type == TERM_TYPE_BIG5) {
+                       if (arr[i] > 0xff)
+                               g_string_append_c(str, (arr[i] >> 8) & 0xff);
+                       g_string_append_c(str, arr[i] & 0xff);
+               } else {
+                       g_string_append_c(str, arr[i]);
+               }
+       }
+
+       gui_entry_set_text(active_entry, str->str);
+       g_string_free(str, TRUE);
+}
+
+static void paste_flush(int send)
+{
+       gui_entry_set_text(active_entry, paste_entry);
+       gui_entry_set_pos(active_entry, paste_entry_pos);
+
+       if (send)
+               paste_send();
+       g_array_set_size(paste_buffer, 0);
+
+       gui_entry_set_prompt(active_entry,
+                            paste_old_prompt == NULL ? "" : paste_old_prompt);
+       g_free(paste_old_prompt); paste_old_prompt = NULL;
+       paste_prompt = FALSE;
+
+       paste_line_count = 0;
+       paste_state = 0;
+        paste_keycount = 0;
+
+       gui_entry_redraw(active_entry);
+}
+
+static gboolean paste_timeout(gpointer data)
+{
+       GTimeVal now;
+       char *str;
+       int diff;
+
+       if (paste_state == 0) {
+               /* gone already */
+               return FALSE;
+       }
+
+        g_get_current_time(&now);
+       diff = (now.tv_sec - last_keypress.tv_sec) * 1000 +
+               (now.tv_usec - last_keypress.tv_usec)/1000;
+
+       if (diff < paste_detect_time) {
+               /* still pasting */
+               return TRUE;
+       }
+
+       if (paste_line_count < paste_verify_line_count ||
+           active_win->active == NULL) {
+               /* paste without asking */
+               paste_flush(TRUE);
+       } else if (!paste_prompt) {
+               paste_prompt = TRUE;
+               paste_old_prompt = g_strdup(active_entry->prompt);
+               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+                                  TXT_PASTE_WARNING,
+                                  paste_line_count,
+                                  active_win->active == NULL ? "window" :
+                                  active_win->active->visible_name);
+
+               str = format_get_text(MODULE_NAME, active_win, NULL, NULL,
+                                     TXT_PASTE_PROMPT, 0, 0);
+               gui_entry_set_prompt(active_entry, str);
+               gui_entry_set_text(active_entry, "");
+               g_free(str);
+       }
+       return TRUE;
+}
+
+static int check_pasting(unichar key, int diff)
+{
+       if (paste_state < 0)
+               return FALSE;
+
+       if (paste_state == 0) {
+               /* two keys hit together quick. possibly pasting */
+               if (diff > paste_detect_time)
+                       return FALSE;
+
+               g_free(paste_entry);
+               paste_entry = g_strdup(prev_entry);
+               paste_entry_pos = prev_entry_pos;
+
+               paste_state++;
+               paste_line_count = 0;
+               paste_keycount = 0;
+               g_array_set_size(paste_buffer, 0);
+               if (prev_key != '\r' && prev_key != '\n')
+                       g_array_append_val(paste_buffer, prev_key);
+       } else if (paste_state > 0 && diff > paste_detect_time &&
+                  paste_line_count == 0) {
+               /* reset paste state */
+               paste_state = 0;
+               paste_keycount = 0;
+               return FALSE;
+       }
+
+       /* continuing quick hits */
+       if ((key == 11 || key == 3) && paste_prompt) {
+               paste_flush(key == 11);
+               return TRUE;
+       }
+
+       g_array_append_val(paste_buffer, key);
+       if (key == '\r' || key == '\n') {
+               if (paste_state == 1 &&
+                   paste_keycount < paste_detect_keycount) {
+                       /* not enough keypresses to determine if this is
+                          pasting or not. don't reset paste keycount, but
+                          send this line as non-pasted */
+                       g_array_set_size(paste_buffer, 0);
+                       return FALSE;
+               }
+
+               /* newline - assume this line was pasted */
+               if (paste_state == 1) {
+                       paste_state = 2;
+                       gui_entry_set_text(active_entry, paste_entry);
+                       gui_entry_set_pos(active_entry, paste_entry_pos);
+                       if (paste_verify_line_count > 0)
+                               g_timeout_add(100, paste_timeout, NULL);
+               }
+
+               if (paste_verify_line_count <= 0) {
+                       /* paste previous line */
+                       paste_send();
+                       g_array_set_size(paste_buffer, 0);
+               } else {
+                       paste_line_count++;
+               }
+       }
+
+       return paste_state == 2;
+}
+
 static void sig_gui_key_pressed(gpointer keyp)
 {
+       GTimeVal now;
         unichar key;
        char str[20];
+       int ret, diff;
 
        key = GPOINTER_TO_INT(keyp);
 
@@ -146,7 +460,14 @@ static void sig_gui_key_pressed(gpointer keyp)
                return;
        }
 
-       idle_time = time(NULL);
+        g_get_current_time(&now);
+       diff = (now.tv_sec - last_keypress.tv_sec) * 1000 +
+               (now.tv_usec - last_keypress.tv_usec)/1000;
+
+       if (check_pasting(key, diff)) {
+               last_keypress = now;
+               return;
+       }
 
        if (key < 32) {
                /* control key */
@@ -158,8 +479,14 @@ static void sig_gui_key_pressed(gpointer keyp)
                str[1] = '?';
                 str[2] = '\0';
        } else if (!active_entry->utf8) {
-               str[0] = (char)key;
-               str[1] = '\0';
+               if (key <= 0xff) {
+                       str[0] = (char)key;
+                       str[1] = '\0';
+               } else {
+                       str[0] = (char) (key >> 8);
+                       str[1] = (char) (key & 0xff);
+                       str[2] = '\0';
+               }
        } else {
                 /* need to convert to utf8 */
                str[utf16_char_to_utf8(key, str)] = '\0';
@@ -171,18 +498,39 @@ static void sig_gui_key_pressed(gpointer keyp)
                str[2] = '\0';
        }
 
-       if (escape_next_key || !key_pressed(keyboard, str)) {
-               /* key wasn't used for anything, print it */
-                escape_next_key = FALSE;
+       g_free(prev_entry);
+       prev_entry = gui_entry_get_text(active_entry);
+       prev_entry_pos = gui_entry_get_pos(active_entry);
+
+       if (escape_next_key) {
+               escape_next_key = FALSE;
                gui_entry_insert_char(active_entry, key);
+               ret = 1;
+       } else {
+               ret = key_pressed(keyboard, str);
+               if (ret < 0) {
+                       /* key wasn't used for anything, print it */
+                       gui_entry_insert_char(active_entry, key);
+               }
        }
+
+       /* ret = 0 : some key create multiple characters - we're in the middle
+          of one. try to detect the keycombo as a single keypress rather than
+          multiple small onces to avoid incorrect paste detection.
+
+          don't count repeated keys so paste detection won't go on when
+          you're holding some key down */
+       if (ret != 0 && key != prev_key) {
+               last_keypress = now;
+               paste_keycount++;
+       }
+       prev_key = key;
 }
 
 static void key_send_line(void)
 {
        HISTORY_REC *history;
         char *str, *add_history;
-       gint flags = (redir ? redir->flags : 0);
 
        str = gui_entry_get_text(active_entry);
 
@@ -198,13 +546,9 @@ static void key_send_line(void)
                            active_win->active_server,
                            active_win->active);
        } else {
-               if (flags & ENTRY_REDIRECT_FLAG_HIDDEN && add_history) {
-                       memset(add_history, 0, strlen(add_history));
+               if (redir->flags & ENTRY_REDIRECT_FLAG_HIDDEN)
                         g_free_and_null(add_history);
-               }
                handle_entry_redirect(str);
-               if (flags & ENTRY_REDIRECT_FLAG_HIDDEN && str)
-                       memset(str, 0, strlen(str));
        }
 
        if (add_history != NULL) {
@@ -326,6 +670,25 @@ static void key_transpose_characters(void)
        gui_entry_transpose_chars(active_entry);
 }
 
+static void key_transpose_words(void)
+{
+       gui_entry_transpose_words(active_entry);
+}
+
+static void key_capitalize_word(void)
+{
+       gui_entry_capitalize_word(active_entry);
+}
+
+static void key_downcase_word(void)
+{
+       gui_entry_downcase_word(active_entry);
+}
+static void key_upcase_word(void)
+{
+       gui_entry_upcase_word(active_entry);
+}
+
 static void key_delete_character(void)
 {
        if (gui_entry_get_pos(active_entry) < active_entry->text_len) {
@@ -384,7 +747,7 @@ static void sig_input(void)
 
 time_t get_idle_time(void)
 {
-       return idle_time;
+       return last_keypress.tv_sec;
 }
 
 static void key_scroll_backward(void)
@@ -417,9 +780,7 @@ static void key_completion(int erase)
        char *text, *line;
        int pos;
 
-       pos = gui_entry_get_pos(active_entry);
-
-        text = gui_entry_get_text(active_entry);
+        text = gui_entry_get_text_and_pos(active_entry, &pos);
        line = word_complete(active_win, text, &pos, erase);
        g_free(text);
 
@@ -445,9 +806,7 @@ static void key_check_replaces(void)
        char *text, *line;
        int pos;
 
-       pos = gui_entry_get_pos(active_entry);
-
-        text = gui_entry_get_text(active_entry);
+        text = gui_entry_get_text_and_pos(active_entry, &pos);
        line = auto_word_complete(text, &pos);
        g_free(text);
 
@@ -636,6 +995,22 @@ static void sig_gui_entry_redirect(SIGNAL_FUNC func, const char *entry,
        gui_entry_set_prompt(active_entry, entry);
 }
 
+static void setup_changed(void)
+{
+       paste_detect_time = settings_get_time("paste_detect_time");
+       if (paste_detect_time == 0)
+               paste_state = -1;
+       else if (paste_state == -1)
+               paste_state = 0;
+
+       paste_detect_keycount = settings_get_int("paste_detect_keycount");
+       if (paste_detect_keycount < 2)
+               paste_state = -1;
+
+       paste_verify_line_count = settings_get_int("paste_verify_line_count");
+       paste_join_multiline = settings_get_bool("paste_join_multiline");
+}
+
 void gui_readline_init(void)
 {
        static char changekeys[] = "1234567890qwertyuio";
@@ -644,10 +1019,24 @@ void gui_readline_init(void)
 
         escape_next_key = FALSE;
        redir = NULL;
-       idle_time = time(NULL);
+       prev_entry = NULL;
+       paste_state = 0;
+        paste_keycount = 0;
+       paste_entry = NULL;
+       paste_entry_pos = 0;
+       paste_buffer = g_array_new(FALSE, FALSE, sizeof(unichar));
+        paste_old_prompt = NULL;
+       g_get_current_time(&last_keypress);
         input_listen_init(STDIN_FILENO);
 
        settings_add_str("history", "scroll_page_count", "/2");
+       settings_add_time("misc", "paste_detect_time", "5msecs");
+       settings_add_int("misc", "paste_detect_keycount", 5);
+       /* NOTE: function keys can generate at least 5 characters long
+          keycodes. this must be larger to allow them to work. */
+       settings_add_int("misc", "paste_verify_line_count", 5);
+       settings_add_bool("misc", "paste_join_multiline", TRUE);
+        setup_changed();
 
        keyboard = keyboard_create(NULL);
         key_configure_freeze();
@@ -714,8 +1103,8 @@ void gui_readline_init(void)
        key_bind("backspace", "", "backspace", NULL, (SIGNAL_FUNC) key_backspace);
        key_bind("delete_character", "", "delete", NULL, (SIGNAL_FUNC) key_delete_character);
        key_bind("delete_character", NULL, "^D", NULL, (SIGNAL_FUNC) key_delete_character);
-       key_bind("delete_next_word", "meta-d", NULL, NULL, (SIGNAL_FUNC) key_delete_next_word);
-       key_bind("delete_previous_word", "meta-backspace", NULL, NULL, (SIGNAL_FUNC) key_delete_previous_word);
+       key_bind("delete_next_word", "", "meta-d", NULL, (SIGNAL_FUNC) key_delete_next_word);
+       key_bind("delete_previous_word", "", "meta-backspace", NULL, (SIGNAL_FUNC) key_delete_previous_word);
        key_bind("delete_to_previous_space", "", "^W", NULL, (SIGNAL_FUNC) key_delete_to_previous_space);
        key_bind("delete_to_next_space", "", "", NULL, (SIGNAL_FUNC) key_delete_to_next_space);
        key_bind("erase_line", "", "^U", NULL, (SIGNAL_FUNC) key_erase_line);
@@ -723,6 +1112,10 @@ void gui_readline_init(void)
        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);
+       key_bind("transpose_words", "Swap current and previous word", NULL, NULL, (SIGNAL_FUNC) key_transpose_words);
+       key_bind("capitalize_word", "Capitalize word", NULL, NULL, (SIGNAL_FUNC) key_capitalize_word);
+       key_bind("downcase_word", "Downcase word", NULL, NULL, (SIGNAL_FUNC) key_downcase_word);
+       key_bind("upcase_word", "Upcase word", NULL, NULL, (SIGNAL_FUNC) key_upcase_word);
 
         /* line transmitting */
        key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line);
@@ -773,6 +1166,7 @@ void gui_readline_init(void)
        signal_add("window changed automatic", (SIGNAL_FUNC) sig_window_auto_changed);
        signal_add("gui entry redirect", (SIGNAL_FUNC) sig_gui_entry_redirect);
        signal_add("gui key pressed", (SIGNAL_FUNC) sig_gui_key_pressed);
+       signal_add("setup changed", (SIGNAL_FUNC) setup_changed);
 }
 
 void gui_readline_deinit(void)
@@ -804,6 +1198,11 @@ void gui_readline_deinit(void)
        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("transpose_words", (SIGNAL_FUNC) key_transpose_words);
+
+       key_unbind("capitalize_word", (SIGNAL_FUNC) key_capitalize_word);
+       key_unbind("downcase_word", (SIGNAL_FUNC) key_downcase_word);
+       key_unbind("upcase_word", (SIGNAL_FUNC) key_upcase_word);
 
        key_unbind("send_line", (SIGNAL_FUNC) key_send_line);
        key_unbind("word_completion", (SIGNAL_FUNC) key_word_completion);
@@ -830,11 +1229,13 @@ void gui_readline_deinit(void)
        key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
        key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
        key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
-        keyboard_destroy(keyboard);
+       keyboard_destroy(keyboard);
+        g_array_free(paste_buffer, TRUE);
 
         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);
        signal_remove("gui key pressed", (SIGNAL_FUNC) sig_gui_key_pressed);
+       signal_remove("setup changed", (SIGNAL_FUNC) setup_changed);
 }
index 61fb5fc1d2a4617af6a30977f1789368c8356599..3919be7ef66eee5c2acae3d15b405a2c3769b55d 100644 (file)
@@ -179,7 +179,8 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist,
 
        if (len > MAX_LINES_WITHOUT_FORCE && fhandle == -1 &&
            g_hash_table_lookup(optlist, "force") == NULL) {
-               printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+               printformat_window(active_win,
+                                  MSGLEVEL_CLIENTNOTICE|MSGLEVEL_LASTLOG,
                                   TXT_LASTLOG_TOO_LONG, len);
                g_list_free(list);
                return;
index 03a8270512a0f2070a356c9b4a10e69e52b3c846..03fdeda86b06c4c5e71751aac52c4a62aa76f9ee 100644 (file)
@@ -154,17 +154,14 @@ void mainwindow_change_active(MAIN_WINDOW_REC *mainwin,
                WINDOW_REC *rec = tmp->data;
 
                if (rec != skip_window) {
-                       if (WINDOW_MAIN(rec) == mainwin) {
-                               window_set_active(rec);
-                               return;
-                       }
-                        other = rec;
+                       other = rec;
+                       break;
                }
        }
 
-       /* no more non-sticky windows, remove main window */
        window_set_active(other);
-       mainwindow_destroy(mainwin);
+       if (mainwindows->next != NULL)
+               mainwindow_destroy(mainwin);
 }
 
 void mainwindows_recreate(void)
@@ -304,11 +301,13 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window)
 
         term_window_destroy(window->screen_win);
 
-       if (!quitting && mainwindows != NULL) {
+       if (mainwindows != NULL) {
                gui_windows_remove_parent(window);
-               mainwindows_add_space(window->first_line, window->last_line);
-
-               mainwindows_redraw();
+               if (!quitting) {
+                       mainwindows_add_space(window->first_line,
+                                             window->last_line);
+                       mainwindows_redraw();
+               }
        }
 
        g_free(window);
index 79e9dc2bb716e6f5a671cf14cb5676ce8f16eed0..f627a2d994dedea4fdd41fad641202910d27fa56 100644 (file)
@@ -65,12 +65,17 @@ FORMAT_REC gui_text_formats[] =
        { "statusbar_info_item_footer", "", 0 },
        { "statusbar_info_item_name",  "%#         : $[35]0 $[9]1 $2", 3, { 0, 1, 0 } },
        { "statusbar_not_found", "Statusbar doesn't exist: $0", 1, { 0 } },
-       { "statusbar_not_found", "Statusbar doesn't exist: $0", 1, { 0 } },
        { "statusbar_item_not_found", "Statusbar item doesn't exist: $0", 1, { 0 } },
        { "statusbar_unknown_command", "Unknown statusbar command: $0", 1, { 0 } },
        { "statusbar_unknown_type", "Statusbar type must be 'window' or 'root'", 1, { 0 } },
        { "statusbar_unknown_placement", "Statusbar placement must be 'top' or 'bottom'", 1, { 0 } },
        { "statusbar_unknown_visibility", "Statusbar visibility must be 'always', 'active' or 'inactive'", 1, { 0 } },
 
+       /* ---- */
+       { NULL, "Pasting", 0 },
+
+       { "paste_warning", "Pasting $0 lines to $1. Press Ctrl-K if you wish to do this or Ctrl-C to cancel.", 2, { 1, 0 } },
+       { "paste_prompt", "Hit Ctrl-K to paste, Ctrl-C to abort?", 0 },
+
        { NULL, NULL, 0 }
 };
index c3607311dd3f3c9bcdffeba1c8c0688dc6ecc2b2..4eebfc3e1e1e39350aa70b25cece8d85e098fe15 100644 (file)
@@ -44,7 +44,14 @@ enum {
        TXT_STATUSBAR_UNKNOWN_COMMAND,
         TXT_STATUSBAR_UNKNOWN_TYPE,
        TXT_STATUSBAR_UNKNOWN_PLACEMENT,
-        TXT_STATUSBAR_UNKNOWN_VISIBILITY
+        TXT_STATUSBAR_UNKNOWN_VISIBILITY,
+
+       TXT_FILL_4,
+
+       TXT_PASTE_WARNING,
+       TXT_PASTE_PROMPT,
+
+       TXT_COUNT
 };
 
-extern FORMAT_REC gui_text_formats[];
+extern FORMAT_REC gui_text_formats[TXT_COUNT+1];
index 1591e601cf767622f0652bad1d721f43a3909cc8..3de8fdc8b06b44477cb97b6654f32152b6ec50c9 100644 (file)
@@ -80,13 +80,13 @@ static GMainLoop *main_loop;
 int quitting;
 
 static const char *firsttimer_text =
-       "Looks like this is the first time you run irssi.\n"
+       "Looks like this is the first time you've 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/";
+       "startup-HOWTO if you haven't already. You can find it\n"
+       "and more irssi beginner info at http://irssi.org/help/\n"
+       "\n"
+       "For the truly impatient people who don't like any automatic\n"
+       "window creation or closing, just type: /MANUAL-WINDOWS";
 static int display_firsttimer = FALSE;
 
 
@@ -176,9 +176,11 @@ static void textui_finish_init(void)
                gui_windows_init();
                statusbar_init();
                term_refresh_thaw();
+
+               /* don't check settings with dummy mode */
+               settings_check();
        }
 
-       settings_check();
        module_register("core", "fe-text");
 
 #ifdef HAVE_STATIC_PERL
@@ -307,6 +309,22 @@ static void winsock_init(void)
 }
 #endif
 
+#ifdef USE_GC
+#ifdef HAVE_GC_H
+#  include <gc.h>
+#else
+#  include <gc/gc.h>
+#endif
+
+GMemVTable gc_mem_table = {
+       GC_malloc,
+       GC_realloc,
+       GC_free,
+
+       NULL, NULL, NULL
+};
+#endif
+
 int main(int argc, char **argv)
 {
        static struct poptOption options[] = {
@@ -316,6 +334,12 @@ int main(int argc, char **argv)
                { NULL, '\0', 0, NULL }
        };
 
+#ifdef USE_GC
+       g_mem_set_vtable(&gc_mem_table);
+#endif
+
+       srand(time(NULL));
+
        dummy = FALSE;
        quitting = FALSE;
        core_init_paths(argc, argv);
@@ -334,13 +358,16 @@ int main(int argc, char **argv)
        textdomain(PACKAGE);
 #endif
 
-        /* setlocale() must be called at the beginning before any callsthat
-           affect it, especially regexps seem to break if they'regenerated
-           before t his call.
+       /* setlocale() must be called at the beginning before any calls that
+          affect it, especially regexps seem to break if they're generated
+          before t his call.
+
+          locales aren't actually used for anything else than autodetection
+          of UTF-8 currently..
 
-           locales aren't actually used for anything else thanautodetection
-           of UTF-8 currently.. */
-        setlocale(LC_CTYPE, "");
+          furthermore to get the users's charset with g_get_charset() properly
+          you have to call setlocale(LC_ALL, "") */
+       setlocale(LC_ALL, "");
 
        textui_init();
        args_register(options);
@@ -358,7 +385,19 @@ int main(int argc, char **argv)
        /* Does the same as g_main_run(main_loop), except we
           can call our dirty-checker after each iteration */
        while (!quitting) {
+#ifdef USE_GC
+               GC_collect_a_little();
+#endif
+               if (!dummy) term_refresh_freeze();
                g_main_iteration(TRUE);
+               if (!dummy) term_refresh_thaw();
+
+               if (reload_config) {
+                       /* SIGHUP received, do /RELOAD */
+                       reload_config = FALSE;
+                       signal_emit("command reload", 1, "");
+               }
+
                 dirty_check();
        }
 
index fe74a748276c42e32f662c2eb47042c5a1610dd1..dce98380d089697fc1a3fc287e8e5f5255459314 100644 (file)
@@ -290,15 +290,16 @@ static void item_lag(SBAR_ITEM_REC *item, int get_size_only)
        int lag, lag_unknown;
 
        server = active_win == NULL ? NULL : active_win->active_server;
-       lag = get_lag(server, &lag_unknown)/10;
+       lag = get_lag(server, &lag_unknown);
 
-       if (lag <= 0 || lag < settings_get_int("lag_min_show")) {
+       if (lag <= 0 || lag < settings_get_time("lag_min_show")) {
                /* don't print the lag item */
                if (get_size_only)
                        item->min_size = item->max_size = 0;
                return;
        }
 
+       lag /= 10;
        last_lag = lag;
        last_lag_unknown = lag_unknown;
 
@@ -321,8 +322,10 @@ static void lag_check_update(void)
        server = active_win == NULL ? NULL : active_win->active_server;
        lag = get_lag(server, &lag_unknown)/10;
 
-       if (lag < settings_get_int("lag_min_show"))
-                lag = 0;
+       if (lag < settings_get_time("lag_min_show"))
+               lag = 0;
+       else
+               lag /= 10;
 
        if (lag != last_lag || (lag > 0 && lag_unknown != last_lag_unknown))
                 statusbar_items_redraw("lag");
@@ -372,7 +375,7 @@ static void read_settings(void)
 
 void statusbar_items_init(void)
 {
-       settings_add_int("misc", "lag_min_show", 100);
+       settings_add_time("misc", "lag_min_show", "1sec");
        settings_add_bool("lookandfeel", "actlist_moves", FALSE);
 
        statusbar_item_register("window", NULL, item_window_active);
index 689e4f9b3416137ec0a643e42e6410954acf83f8..5eba9557f9ace85104067cf6b8687e3a930794ef 100644 (file)
@@ -684,8 +684,8 @@ void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
                                    int escape_vars)
 {
        SERVER_REC *server;
-       WI_ITEM_REC *wiitem;
-        char *tmpstr, *tmpstr2;
+       WI_ITEM_REC *wiitem; 
+       char *tmpstr, *tmpstr2;
        int len;
 
        if (str == NULL)
index 63db46e542bccc457f393531e45e2ed6bc5b6ac4..9306d030a52cb4204efed99c0c10de817e351de7 100644 (file)
 
 #include "term.h"
 #include "mainwindows.h"
-
-#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
-#  include <ncurses.h>
-#else
-#  include <curses.h>
+#ifdef HAVE_CUIX
+#include "cuix.h"
 #endif
+
+#include "term-curses.h"
+
 #include <termios.h>
 #include <signal.h>
 
 #  define _POSIX_VDISABLE 0
 #endif
 
-struct _TERM_WINDOW {
-       int x, y;
-        int width, height;
-       WINDOW *win;
-};
-
 TERM_WINDOW *root_window;
 
 static int curs_x, curs_y;
@@ -276,8 +270,8 @@ static int get_attr(int color)
 
        if (!term_use_colors)
                attr = (color & 0x70) ? A_REVERSE : 0;
-       else if (((color & 0x0f) == 8) && (color & ATTR_BOLD) == 0)
-                attr = (A_DIM | COLOR_PAIR(63));
+       else if ((color & 0xff) == 8 || (color & (0xff | ATTR_RESETFG)) == 0)
+               attr = COLOR_PAIR(63);
        else if ((color & 0x77) == 0)
                attr = A_NORMAL;
        else {
@@ -285,7 +279,7 @@ static int get_attr(int color)
                        color &= ~0x0f;
                        color |= settings_get_int("default_color");
                }
-               attr = (COLOR_PAIR((color&7) + (color&0x70)/2));
+               attr = COLOR_PAIR((color&7) | ((color&0x70)>>1));
        }
 
        if ((color & 0x08) || (color & ATTR_BOLD)) attr |= A_BOLD;
@@ -315,6 +309,15 @@ void term_addch(TERM_WINDOW *window, int chr)
 
 void term_add_unichar(TERM_WINDOW *window, unichar chr)
 {
+#ifdef WIDEC_CURSES
+       cchar_t wch;
+       wchar_t temp[2];
+       temp[0] = chr;
+       temp[1] = 0;
+       if (setcchar(&wch, temp, A_NORMAL, 0, NULL) == OK)
+               wadd_wch(window->win, &wch);
+       else
+#endif
         waddch(window->win, chr);
 }
 
@@ -349,20 +352,32 @@ void term_refresh_thaw(void)
 
 void term_refresh(TERM_WINDOW *window)
 {
+#ifdef HAVE_CUIX
+    if (!cuix_active) {
+#endif
        if (window != NULL)
                wnoutrefresh(window->win);
 
        if (freeze_refresh == 0) {
                move(curs_y, curs_x);
                wnoutrefresh(stdscr);
+#ifdef HAVE_CUIX
+                cuix_refresh ();
+#endif
                doupdate();
        }
+#ifdef HAVE_CUIX
+    } else {
+        update_panels ();
+        doupdate ();
+    }
+#endif
 }
 
 void term_stop(void)
 {
        term_deinit_int();
-       kill(getpid(), SIGSTOP);
+       kill(getpid(), SIGTSTP);
         term_init_int();
        irssi_redraw();
 }
@@ -377,20 +392,26 @@ void term_set_input_type(int type)
 
 int term_gets(unichar *buffer, int size)
 {
-       int key, count;
+       int count;
+#ifdef WIDEC_CURSES
+       wint_t key;
+#else
+       int key;
+#endif
 
        for (count = 0; count < size; ) {
-               key = getch();
+#ifdef WIDEC_CURSES
+               if (get_wch(&key) == ERR)
+#else
+               if ((key = getch()) == ERR)
+#endif
+                       break;
 #ifdef KEY_RESIZE
                if (key == KEY_RESIZE)
                        continue;
 #endif
 
-               if (key == ERR)
-                       break;
-
-                buffer[count] = key;
-                count++;
+               buffer[count++] = key;
        }
 
        return count;
diff --git a/apps/irssi/src/fe-text/term-curses.h b/apps/irssi/src/fe-text/term-curses.h
new file mode 100644 (file)
index 0000000..9d9712b
--- /dev/null
@@ -0,0 +1,16 @@
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+#  include <ncurses.h>
+#else
+#  include <curses.h>
+#endif
+
+#ifdef HAVE_CUIX
+#include <form.h>
+#include <panel.h>
+#endif
+
+struct _TERM_WINDOW {
+       int x, y;
+        int width, height;
+       WINDOW *win;
+};
index 311b249991d06dd5f79eaef8b6469ec896c0eafd..feeb62c30bd67e05b16fba6371f9cee002766564 100644 (file)
@@ -339,7 +339,7 @@ void term_set_color(TERM_WINDOW *window, int col)
        /* bold */
        if (col & 0x08)
                col |= ATTR_BOLD;
-       else if (col & ATTR_BOLD)
+       if (col & ATTR_BOLD)
                terminfo_set_bold();
 
        /* underline */
@@ -354,6 +354,7 @@ void term_set_color(TERM_WINDOW *window, int col)
 
 void term_move(TERM_WINDOW *window, int x, int y)
 {
+       if (x >= 0 && y >= 0) {
        vcmove = TRUE;
        vcx = x+window->x;
         vcy = y+window->y;
@@ -362,6 +363,7 @@ void term_move(TERM_WINDOW *window, int x, int y)
                vcx = term_width-1;
        if (vcy >= term_height)
                 vcy = term_height-1;
+       }
 }
 
 static void term_printed_text(int count)
@@ -421,16 +423,22 @@ void term_add_unichar(TERM_WINDOW *window, unichar chr)
        if (vcy == term_height-1 && vcx == term_width-1)
                return; /* last char in screen */
 
-        term_printed_text(1);
        switch (term_type) {
        case TERM_TYPE_UTF8:
+               term_printed_text(utf8_width(chr));
                 term_addch_utf8(window, chr);
                break;
        case TERM_TYPE_BIG5:
-               putc((chr >> 8) & 0xff, window->term->out);
+               if (chr > 0xff) {
+                       term_printed_text(2);
+                       putc((chr >> 8) & 0xff, window->term->out);
+               } else {
+                       term_printed_text(1);
+               }
                putc((chr & 0xff), window->term->out);
                 break;
        default:
+               term_printed_text(1);
                putc(chr, window->term->out);
                 break;
        }
@@ -443,7 +451,7 @@ void term_addstr(TERM_WINDOW *window, const char *str)
        if (term_detached) return;
 
        if (vcmove) term_move_real();
-       len = strlen(str);
+       len = strlen(str); /* FIXME utf8 or big5 */
         term_printed_text(len);
 
        if (vcy != term_height || vcx != 0)
@@ -537,10 +545,10 @@ void term_attach(FILE *in, FILE *out)
 void term_stop(void)
 {
        if (term_detached) {
-               kill(getpid(), SIGSTOP);
+               kill(getpid(), SIGTSTP);
        } else {
                terminfo_stop(current_term);
-               kill(getpid(), SIGSTOP);
+               kill(getpid(), SIGTSTP);
                terminfo_cont(current_term);
                irssi_redraw();
        }
@@ -550,13 +558,12 @@ static int input_utf8(const unsigned char *buffer, int size, unichar *result)
 {
         const unsigned char *end = buffer;
 
-        *result = get_utf8_char(&end, size);
-       switch (*result) {
-       case (unichar) -2:
+       switch (get_utf8_char(&end, size, result)) {
+       case -2:
                /* not UTF8 - fallback to 8bit ascii */
                *result = *buffer;
                return 1;
-       case (unichar) -1:
+       case -1:
                 /* need more data */
                return -1;
        default:
@@ -564,12 +571,6 @@ static int input_utf8(const unsigned char *buffer, int size, unichar *result)
        }
 }
 
-/* XXX I didn't check the encoding range of big5+. This is standard big5. */
-#define is_big5_los(lo) (0x40 <= (lo) && (lo) <= 0x7E) /* standard */
-#define is_big5_lox(lo) (0x80 <= (lo) && (lo) <= 0xFE) /* extended */
-#define is_big5_hi(hi)  (0x81 <= (hi) && (hi) <= 0xFE)
-#define is_big5(hi,lo) (is_big5_hi(hi) && (is_big5_los(lo) || is_big5_lox(lo)))
-
 static int input_big5(const unsigned char *buffer, int size, unichar *result)
 {
        if (is_big5_hi(*buffer)) {
@@ -646,7 +647,7 @@ int term_gets(unichar *buffer, int size)
                if (i >= term_inbuf_pos)
                        term_inbuf_pos = 0;
                else if (i > 0) {
-                       memmove(term_inbuf+i, term_inbuf, term_inbuf_pos-i);
+                       memmove(term_inbuf, term_inbuf+i, term_inbuf_pos-i);
                         term_inbuf_pos -= i;
                }
        }
index a36e68fac142ec7187d0e2b3e699d1b9245d63e9..59c3fe4d49406f0025f91f8ee5192946d12ad436 100644 (file)
@@ -105,6 +105,11 @@ static void cmd_resize(void)
         term_resize_dirty();
 }
 
+static void cmd_redraw(void)
+{
+       irssi_redraw();
+}
+
 static void read_settings(void)
 {
         const char *str;
@@ -114,7 +119,7 @@ static void read_settings(void)
         term_auto_detach(settings_get_bool("term_auto_detach"));
 
         /* set terminal type */
-       str = settings_get_str("term_type");
+       str = settings_get_str("term_charset");
        if (g_strcasecmp(str, "utf-8") == 0)
                term_type = TERM_TYPE_UTF8;
        else if (g_strcasecmp(str, "big5") == 0)
@@ -147,7 +152,6 @@ void term_common_init(void)
        settings_add_bool("lookandfeel", "term_force_colors", FALSE);
         settings_add_bool("lookandfeel", "term_auto_detach", FALSE);
         settings_add_bool("lookandfeel", "mirc_blink_fix", FALSE);
-        settings_add_str("lookandfeel", "term_type", "8bit");
 
        force_colors = FALSE;
        term_use_colors = term_has_colors() && settings_get_bool("colors");
@@ -163,6 +167,7 @@ void term_common_init(void)
        signal_add("beep", (SIGNAL_FUNC) term_beep);
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
        command_bind("resize", NULL, (SIGNAL_FUNC) cmd_resize);
+       command_bind("redraw", NULL, (SIGNAL_FUNC) cmd_redraw);
 
 #ifdef SIGWINCH
        sigemptyset (&act.sa_mask);
@@ -175,6 +180,7 @@ void term_common_init(void)
 void term_common_deinit(void)
 {
        command_unbind("resize", (SIGNAL_FUNC) cmd_resize);
+       command_unbind("redraw", (SIGNAL_FUNC) cmd_redraw);
        signal_remove("beep", (SIGNAL_FUNC) term_beep);
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
index 524fb9d0156502a3da1effd07416386284562e64..530050c2a4200c66a10b2792d5d6299449930daf 100644 (file)
@@ -416,23 +416,23 @@ void terminfo_setup_colors(TERM_REC *term, int force)
        terminfo_colors_deinit(term);
         term->has_colors = term->TI_setf || term->TI_setaf;
 
-       if (term->TI_setf) {
-               for (i = 0; i < 8; i++)
-                        term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0));
-       } else if (term->TI_setaf) {
+       if (term->TI_setaf) {
                for (i = 0; i < 8; i++)
                         term->TI_fg[i] = g_strdup(tparm(term->TI_setaf, ansitab[i], 0));
+       } else if (term->TI_setf) {
+               for (i = 0; i < 8; i++)
+                        term->TI_fg[i] = g_strdup(tparm(term->TI_setf, i, 0));
        } else if (force) {
                for (i = 0; i < 8; i++)
                         term->TI_fg[i] = g_strdup_printf("\033[%dm", 30+ansitab[i]);
        }
 
-       if (term->TI_setb) {
-               for (i = 0; i < 8; i++)
-                        term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0));
-       } else if (term->TI_setab) {
+       if (term->TI_setab) {
                for (i = 0; i < 8; i++)
                         term->TI_bg[i] = g_strdup(tparm(term->TI_setab, ansitab[i], 0));
+       } else if (term->TI_setb) {
+               for (i = 0; i < 8; i++)
+                        term->TI_bg[i] = g_strdup(tparm(term->TI_setb, i, 0));
        } else if (force) {
                for (i = 0; i < 8; i++)
                         term->TI_bg[i] = g_strdup_printf("\033[%dm", 40+ansitab[i]);
@@ -530,7 +530,7 @@ static int term_setup(TERM_REC *term)
        if (tgetent(term->buffer1, term_env) < 1)
        {
                fprintf(term->out, "Termcap not found for TERM=%s\n", term_env);
-               return -1;
+               return 0;
        }
 #endif
 
index 92eaaec700dba9700fc169d512f29cad55606865..3636510bb2ddd9fd7d92ede76324847f127a8472 100644 (file)
 #include "printtext.h"
 #include "gui-windows.h"
 #include "textbuffer-reformat.h"
+#ifdef HAVE_CUIX
+#include "cuix.h"
+#endif
 
 /* SYNTAX: CLEAR [-all] [<refnum>] */
 static void cmd_clear(const char *data)
 {
-        WINDOW_REC *window;
+       WINDOW_REC *window;
        GHashTable *optlist;
-        char *refnum;
+       char *refnum;
        void *free_arg;
-        GSList *tmp;
+       GSList *tmp;
 
        g_return_if_fail(data != NULL);
 
@@ -46,19 +49,18 @@ static void cmd_clear(const char *data)
                            "clear", &optlist, &refnum)) return;
 
        if (g_hash_table_lookup(optlist, "all") != NULL) {
-                /* clear all windows */
+               /* clear all windows */
                for (tmp = windows; tmp != NULL; tmp = tmp->next) {
-                       WINDOW_REC *window = tmp->data;
-
+                       window = tmp->data;
                        textbuffer_view_clear(WINDOW_GUI(window)->view);
                }
        } else if (*refnum != '\0') {
-                /* clear specified window */
+               /* clear specified window */
                window = window_find_refnum(atoi(refnum));
-                if (window != NULL)
+               if (window != NULL)
                        textbuffer_view_clear(WINDOW_GUI(window)->view);
        } else {
-                /* clear active window */
+               /* clear active window */
                textbuffer_view_clear(WINDOW_GUI(active_win)->view);
        }
 
@@ -97,10 +99,37 @@ static void cmd_scrollback(const char *data, SERVER_REC *server,
        command_runsub("scrollback", data, server, item);
 }
 
-/* SYNTAX: SCROLLBACK CLEAR */
-static void cmd_scrollback_clear(void)
+/* SYNTAX: SCROLLBACK CLEAR [-all] [<refnum>] */
+static void cmd_scrollback_clear(const char *data)
 {
-       textbuffer_view_remove_all_lines(WINDOW_GUI(active_win)->view);
+       WINDOW_REC *window;
+       GHashTable *optlist;
+       char *refnum;
+       void *free_arg;
+       GSList *tmp;
+
+       g_return_if_fail(data != NULL);
+
+       if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
+                           "scrollback clear", &optlist, &refnum)) return;
+
+       if (g_hash_table_lookup(optlist, "all") != NULL) {
+               /* clear all windows */
+               for (tmp = windows; tmp != NULL; tmp = tmp->next) {
+                       window = tmp->data;
+                       textbuffer_view_remove_all_lines(WINDOW_GUI(window)->view);
+               }
+       } else if (*refnum != '\0') {
+               /* clear specified window */
+               window = window_find_refnum(atoi(refnum));
+               if (window != NULL)
+                       textbuffer_view_remove_all_lines(WINDOW_GUI(window)->view);
+       } else {
+               /* clear active window */
+               textbuffer_view_remove_all_lines(WINDOW_GUI(active_win)->view);
+       }
+
+       cmd_params_free(free_arg);
 }
 
 static void scrollback_goto_line(int linenum)
@@ -303,6 +332,23 @@ static void sig_away_changed(SERVER_REC *server)
        }
 }
 
+#ifdef HAVE_CUIX
+static void cmd_cuix(void)
+{
+    if (!cuix_active)
+    {
+        /* textbuffer_view_clear(WINDOW_GUI(active_win)->view); */
+        cuix_active = 1;
+        cuix_create();
+    } else {
+        /* should never been called */
+        /* cuix_destroy (); */
+        cuix_active = 0;
+        /* textbuffer_view_clear(WINDOW_GUI(active_win)->view); */
+    }
+}
+#endif
+
 void textbuffer_commands_init(void)
 {
        command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
@@ -314,8 +360,12 @@ void textbuffer_commands_init(void)
        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);
+#ifdef HAVE_CUIX
+       command_bind("cuix", NULL, (SIGNAL_FUNC) cmd_cuix);
+#endif
 
        command_set_options("clear", "all");
+       command_set_options("scrollback clear", "all");
 
        signal_add("away mode changed", (SIGNAL_FUNC) sig_away_changed);
 }
@@ -331,6 +381,9 @@ void textbuffer_commands_deinit(void)
        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);
+#ifdef HAVE_CUIX
+       command_unbind("cuix", (SIGNAL_FUNC) cmd_cuix);
+#endif
 
        signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed);
 }
index 62f17f0d0e4e97e6d30827f523510f82c05a1704..81c4421c6ae6cfb572a2526374fa7499be92f9d0 100644 (file)
@@ -87,7 +87,8 @@ static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
        text = (const unsigned char *) line->text;
 
        /* skip the beginning of the line until we find the format */
-       g_free(line_read_format(&text));
+       format_name = line_read_format(&text);
+       g_free(format_name);
        if (text[1] == LINE_CMD_FORMAT_CONT) {
                if (raw != NULL) {
                        g_string_append_c(raw, '\0');
index 77b21a2e512c00f470db5ff0fd55616454137b85..175bd4f6dc1f366f06ce96df6c0c4d0ea2f0675a 100644 (file)
@@ -23,6 +23,9 @@
 #include "module.h"
 #include "textbuffer-view.h"
 #include "utf8.h"
+#ifdef HAVE_CUIX
+#include "cuix.h"
+#endif
 
 typedef struct {
        char *name;
@@ -199,16 +202,20 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                }
 
                if (!view->utf8) {
-                       next_ptr = ptr+1;
-                       char_len = 1;
+                       /* MH */
+                       if (term_type != TERM_TYPE_BIG5 ||
+                           ptr[1] == '\0' || !is_big5(ptr[0], ptr[1]))
+                               char_len = 1;
+                       else
+                               char_len = 2;
+                       next_ptr = ptr+char_len;
                } else {
                        char_len = 1;
                        while (ptr[char_len] != '\0' && char_len < 6)
                                char_len++;
 
                        next_ptr = ptr;
-                       chr = get_utf8_char(&next_ptr, char_len);
-                       if (chr < 0)
+                       if (get_utf8_char(&next_ptr, char_len, &chr) < 0)
                                char_len = 1;
                        else
                                char_len = utf8_width(chr);
@@ -251,7 +258,11 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                        continue;
                }
 
-               if (*ptr == ' ') {
+               if (!view->utf8 && char_len > 1) {
+                       last_space = xpos;
+                       last_space_ptr = next_ptr;
+                       last_color = color;
+               } else if (*ptr == ' ') {
                        last_space = xpos;
                        last_space_ptr = ptr;
                        last_color = color;
@@ -268,11 +279,13 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
 
        if (rec->count > 1) {
                for (pos = 0; lines != NULL; pos++) {
-                       memcpy(&rec->lines[pos], lines->data,
+                       void *data = lines->data;
+
+                       memcpy(&rec->lines[pos], data,
                               sizeof(LINE_CACHE_SUB_REC));
 
-                       g_free(lines->data);
-                       lines = g_slist_remove(lines, lines->data);
+                       lines = g_slist_remove(lines, data);
+                       g_free(data);
                }
        }
 
@@ -421,10 +434,18 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
 
                end = text;
                if (view->utf8) {
-                       unichar chr = get_utf8_char(&end, 6);
-                       char_width = utf8_width(chr);
+                       unichar chr;
+                       if (get_utf8_char(&end, 6, &chr)<0)
+                               char_width = 1;
+                       else
+                               char_width = utf8_width(chr);
                } else {
-                       char_width = 1;
+                       if (term_type == TERM_TYPE_BIG5 &&
+                           is_big5(end[0], end[1]))
+                               char_width = 2;
+                       else
+                               char_width = 1;
+                       end += char_width-1;
                }
 
                xpos += char_width;
@@ -1040,6 +1061,8 @@ static void view_bookmarks_check(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
                        if (new_line != NULL) {
                                g_hash_table_insert(view->bookmarks,
                                                    tmp->data, new_line);
+                       } else {
+                               g_free(tmp->data);
                        }
                }
                g_slist_free(rec.remove_list);
@@ -1120,11 +1143,15 @@ static void view_remove_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
 
                if (view->startline == line) {
                         /* removing the first line in screen */
+                       int is_last = view->startline->next == NULL;
+
                        realcount = view_scroll(view, &view->startline,
                                                &view->subline,
                                                linecount, FALSE);
                        view->ypos -= realcount;
                        view->empty_linecount += linecount-realcount;
+                       if (is_last == 1)
+                               view->startline = NULL;
                }
        } else {
                if (textbuffer_line_exists_after(view->bottom_startline,
@@ -1299,6 +1326,9 @@ static int sig_check_linecache(void)
 void textbuffer_view_init(void)
 {
        linecache_tag = g_timeout_add(LINE_CACHE_CHECK_TIME, (GSourceFunc) sig_check_linecache, NULL);
+#ifdef HAVE_CUIX
+        cuix_active = 0;
+#endif
 }
 
 void textbuffer_view_deinit(void)
index e989b8fda140c853ed205a6f71dc91e415039ba5..930e55ad6d205365adc909c808232f313243c5ef 100644 (file)
@@ -113,15 +113,16 @@ 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;
+        unsigned char cmd, *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)
+               cmd = *text;
+               if (cmd == LINE_CMD_CONTINUE || cmd == LINE_CMD_EOL) {
+                       if (cmd == LINE_CMD_CONTINUE)
                                memcpy(&tmp, text+1, sizeof(char *));
 
                        /* free the previous block */
@@ -133,10 +134,12 @@ static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
                                        text_chunk_destroy(buffer, chunk);
                        }
 
-                       if (*text == LINE_CMD_EOL)
+                       if (cmd == LINE_CMD_EOL)
                                break;
 
                        text = tmp-1;
+               } else if (cmd == LINE_CMD_INDENT_FUNC) {
+                       text += sizeof(int (*) ());
                }
        }
 }
diff --git a/apps/irssi/src/fe-text/utf8.h b/apps/irssi/src/fe-text/utf8.h
deleted file mode 100644 (file)
index 64c0e34..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef __UTF8_H
-#define __UTF8_H
-
-/* Returns -2 = invalid, -1 = need more data, otherwise unichar. */
-unichar get_utf8_char(const unsigned char **ptr, int len);
-
-/* Returns length of UTF8 string */
-int strlen_utf8(const char *str);
-
-/* UTF-8 -> unichar string. The NUL is copied as well. */
-void utf8_to_utf16(const char *str, unichar *out);
-
-/* unichar -> UTF-8 string. outbuf must be at least 6 chars long.
-   Returns outbuf string length. */
-int utf16_char_to_utf8(unichar c, char *outbuf);
-
-/* unichar -> UTF-8 string. The NUL is copied as well.
-   Make sure out is at least 6 x length of str. */
-void utf16_to_utf8(const unichar *str, char *out);
-
-/* Returns width for character (0-2). */
-int utf8_width(unichar c);
-
-#endif
index 02eb4255f5ba4c5d6b9a5ef752589c166c1ff36c..474b007f14ad22af9a26192d8354bbf113b50109 100644 (file)
@@ -252,6 +252,7 @@ void config_parse_init(CONFIG_REC *rec, const char *name)
 
        rec->scanner = scanner = g_scanner_new(NULL);
        scanner->config->skip_comment_single = FALSE;
+       scanner->config->cset_identifier_first = G_CSET_a_2_z"_0123456789"G_CSET_A_2_Z;
        scanner->config->cset_skip_characters = " \t";
        scanner->config->scan_binary = FALSE;
        scanner->config->scan_octal = FALSE;
index ac8fa7e7f47d22d900d1e045216e28e4fcb8f08a..c00e8d27aa603574b8352498fa5bef1fe5a21b26 100644 (file)
@@ -12,8 +12,8 @@ EXTRA_LTLIBRARIES = \
        libperl_core.la libfe_perl.la \
        libperl_core_static.la libfe_perl_static.la
 
-libperl_core_la_LDFLAGS = -avoid-version -rpath $(moduledir)
-libfe_perl_la_LDFLAGS = -avoid-version -rpath $(moduledir)
+libperl_core_la_LDFLAGS = -module -avoid-version -rpath $(moduledir)
+libfe_perl_la_LDFLAGS = -module -avoid-version -rpath $(moduledir)
 
 perl-core.c: perl-signals-list.h irssi-core.pl.h
 
@@ -136,9 +136,10 @@ all-local:
        for dir in $(perl_dirs); do \
          cd $$dir && \
          if [ ! -f Makefile ]; then \
-            $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
+           $(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
          fi && \
-         ($(MAKE) || $(MAKE)) && \
+         ($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \
+          $(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \
          cd ..; \
        done
 
@@ -156,8 +157,8 @@ clean-generic:
 
 distclean-generic:
        for dir in $(perl_dirs); do \
-         cd $$dir && \
-         $(MAKE) realclean && rm -f Makefile.PL && \
+         cd $$dir; \
+         $(MAKE) realclean; rm -f Makefile.PL Makefile; \
          cd ..; \
        done
 
index d5b9d6e80789d6b7f198f6804a42554833b57959..451a07e2d735b8a78a20b6e125be9b81a409c9ca 100644 (file)
@@ -3,6 +3,7 @@
 #include "core.h"
 
 #include "pidwait.h"
+#include "session.h"
 
 #define DEFAULT_COMMAND_CATEGORY "Perl scripts' commands"
 
@@ -74,6 +75,8 @@ CODE:
                        p[n-1] = irssi_ref_object(ST(n));
                else if (SvROK(ST(n)))
                        p[n-1] = (void *) SvIV((SV*)SvRV(ST(n)));
+               else if (SvIOK(ST(n)))
+                       p[n-1] = (void *)SvIV(ST(n));
                else
                        p[n-1] = NULL;
        }
@@ -93,6 +96,8 @@ CODE:
                        p[n] = irssi_ref_object(ST(n));
                else if (SvROK(ST(n)))
                        p[n] = (void *) SvIV((SV*)SvRV(ST(n)));
+               else if (SvIOK(ST(n)))
+                       p[n] = (void *) SvIV(ST(n));
                else
                        p[n] = NULL;
        }
@@ -141,6 +146,39 @@ CODE:
        else
                perl_signal_add_hash(SvIV(ST(0)), ST(1));
 
+void
+signal_register(...)
+PREINIT:
+       HV *hv;
+        HE *he;
+       I32 len, pos;
+       const char *arr[7];
+CODE:
+       if (items != 1 || !is_hvref(ST(0)))
+               croak("Usage: Irssi::signal_register(hash)");
+
+        hv = hvref(ST(0));
+       hv_iterinit(hv);
+       while ((he = hv_iternext(hv)) != NULL) {
+               const char *key = hv_iterkey(he, &len);
+               SV *val = HeVAL(he);
+               AV *av;
+
+               if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVAV)
+                       croak("not array reference");
+
+               av = (AV *) SvRV(val);
+               len = av_len(av)+1;
+               if (len > 6) len = 6;
+               for (pos = 0; pos < len; pos++) {
+                       SV **val = av_fetch(av, pos, 0);
+                       arr[pos] = SvPV(*val, PL_na);
+               }
+               arr[pos] = NULL;
+               perl_signal_register(key, arr);
+       }
+
+
 int
 SIGNAL_PRIORITY_LOW()
 CODE:
@@ -548,6 +586,13 @@ CODE:
 OUTPUT:
        RETVAL
 
+char *
+get_irssi_binary()
+CODE:
+       RETVAL = irssi_binary;
+OUTPUT:
+       RETVAL
+
 char *
 version()
 PREINIT:
index d0e8242566127df92852286f42f2c17ee48a0350..9f33d557191a9dab03687f74c81f5f1cd417683b 100644 (file)
@@ -28,6 +28,7 @@ CODE:
        if (!initialized) return;
        perl_expando_deinit();
         perl_settings_deinit();
+       initialized = FALSE;
 
 BOOT:
         irssi_boot(Channel);
index facf1da0084f01b37327bc13ca4825b9159d3c3b..76ea781218a1778d9b231955950e38b933d2044c 100644 (file)
@@ -72,7 +72,7 @@ isnickflag(server, flag)
        Irssi::Server server
        char flag
 CODE:
-       RETVAL = server->isnickflag(flag);
+       RETVAL = server->isnickflag(server, flag);
 OUTPUT:
        RETVAL
 
@@ -89,7 +89,7 @@ char *
 get_nick_flags(server)
        Irssi::Server server
 CODE:
-       RETVAL = (char *) server->get_nick_flags();
+       RETVAL = (char *) server->get_nick_flags(server);
 OUTPUT:
        RETVAL
 
index a7c5e6f62c8070e21a21449a019b652fb6f9736a..a9bdd11df936b075d34fe2abcdf6ab05575eb3a8 100644 (file)
@@ -1,5 +1,6 @@
 #include "module.h"
 #include "misc.h"
+#include "recode.h"
 
 static GHashTable *perl_settings;
 
@@ -68,11 +69,16 @@ void perl_settings_deinit(void)
 MODULE = Irssi::Settings  PACKAGE = Irssi
 PROTOTYPES: ENABLE
 
-char *
+SV *
 settings_get_str(key)
        char *key
+PREINIT:
+       const char *str;
 CODE:
-       RETVAL = (char *) settings_get_str(key);
+       str = settings_get_str(key);
+       RETVAL = new_pv(str);
+       if (is_utf8())
+               SvUTF8_on(RETVAL);
 OUTPUT:
        RETVAL
 
@@ -84,6 +90,18 @@ int
 settings_get_bool(key)
        char *key
 
+int
+settings_get_time(key)
+       char *key
+
+int
+settings_get_level(key)
+       char *key
+
+int
+settings_get_size(key)
+       char *key
+
 void
 settings_set_str(key, value)
        char *key
@@ -99,6 +117,21 @@ settings_set_bool(key, value)
        char *key
        int value
 
+int
+settings_set_time(key, value)
+       char *key
+       char *value
+
+int
+settings_set_level(key, value)
+       char *key
+       char *value
+
+int
+settings_set_size(key, value)
+       char *key
+       char *value
+
 void
 settings_add_str(section, key, def)
        char *section
@@ -126,6 +159,33 @@ CODE:
         perl_settings_add(key);
        settings_add_bool_module(MODULE_NAME"/scripts", section, key, def);
 
+void
+settings_add_time(section, key, def)
+       char *section
+       char *key
+       char *def
+CODE:
+        perl_settings_add(key);
+       settings_add_time_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_add_level(section, key, def)
+       char *section
+       char *key
+       char *def
+CODE:
+        perl_settings_add(key);
+       settings_add_level_module(MODULE_NAME"/scripts", section, key, def);
+
+void
+settings_add_size(section, key, def)
+       char *section
+       char *key
+       char *def
+CODE:
+        perl_settings_add(key);
+       settings_add_size_module(MODULE_NAME"/scripts", section, key, def);
+
 void
 settings_remove(key)
        char *key
index df4667cef659928322144880390ae42e363ee74f..c61c81cc934a391bef7c3eef0ae5f8a9e75d4fab 100755 (executable)
@@ -51,6 +51,9 @@ while (<STDIN>) {
        s/WINDOW_REC[^,]*/Irssi::UI::Window/g;
        s/WI_ITEM_REC[^,]*/iobject/g;
 
+       # perl
+       s/PERL_SCRIPT_REC[^,]*/Irssi::Script/g;
+
        s/([\w\*:]+)(,|$)/"\1"\2/g;
        print "    { \"$signal\", { $_, NULL } },\n";
 }
index f7cd87da8f2a4060b2b05c7b2da3fce5e0ab0075..03dcc01ec6778f864b2de1eaa614d24e9a433934 100644 (file)
@@ -40,6 +40,7 @@
 #include "queries.h"
 #include "nicklist.h"
 
+#include "perl-core.h"
 #include "perl-common.h"
 
 typedef struct {
@@ -94,6 +95,31 @@ SV *perl_func_sv_inc(SV *func, const char *package)
         return func;
 }
 
+static int magic_free_object(pTHX_ SV *sv, MAGIC *mg)
+{
+       sv_setiv(sv, 0);
+       return 0;
+}
+
+static MGVTBL vtbl_free_object =
+{
+    NULL, NULL, NULL, NULL, magic_free_object
+};
+
+static SV *create_sv_ptr(void *object)
+{
+       SV *sv;
+
+       sv = newSViv((IV)object);
+
+       sv_magic(sv, NULL, '~', NULL, 0);
+
+       SvMAGIC(sv)->mg_private = 0x1551; /* HF */
+       SvMAGIC(sv)->mg_virtual = &vtbl_free_object;
+
+       return sv;
+}
+
 SV *irssi_bless_iobject(int type, int chat_type, void *object)
 {
         PERL_OBJECT_REC *rec;
@@ -106,13 +132,13 @@ SV *irssi_bless_iobject(int type, int chat_type, void *object)
                                  GINT_TO_POINTER(type | (chat_type << 16)));
        if (rec == NULL) {
                 /* unknown iobject */
-               return newSViv((IV)object);
+               return create_sv_ptr(object);
        }
 
        stash = gv_stashpv(rec->stash, 1);
 
        hv = newHV();
-       hv_store(hv, "_irssi", 6, newSViv((IV)object), 0);
+       hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
         rec->fill_func(hv, object);
        return sv_bless(newRV_noinc((SV*)hv), stash);
 }
@@ -125,7 +151,7 @@ SV *irssi_bless_plain(const char *stash, void *object)
        fill_func = g_hash_table_lookup(plain_stashes, stash);
 
        hv = newHV();
-       hv_store(hv, "_irssi", 6, newSViv((IV)object), 0);
+       hv_store(hv, "_irssi", 6, create_sv_ptr(object), 0);
        if (fill_func != NULL)
                fill_func(hv, object);
        return sv_bless(newRV_noinc((SV*)hv), gv_stashpv((char *)stash, 1));
@@ -150,6 +176,7 @@ void *irssi_ref_object(SV *o)
 {
         SV **sv;
        HV *hv;
+       void *p;
 
         hv = hvref(o);
        if (hv == NULL)
@@ -157,8 +184,9 @@ void *irssi_ref_object(SV *o)
 
        sv = hv_fetch(hv, "_irssi", 6, 0);
        if (sv == NULL)
-                croak("variable is damaged");
-       return GINT_TO_POINTER(SvIV(*sv));
+               croak("variable is damaged");
+       p = GINT_TO_POINTER(SvIV(*sv));
+       return p;
 }
 
 void irssi_add_object(int type, int chat_type, const char *stash,
@@ -265,6 +293,7 @@ void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
        hv_store(hv, "type", 4, new_pv(type), 0);
        hv_store(hv, "chat_type", 9, new_pv(chat_type), 0);
 
+       hv_store(hv, "tag", 3, new_pv(conn->tag), 0);
        hv_store(hv, "address", 7, new_pv(conn->address), 0);
        hv_store(hv, "port", 4, newSViv(conn->port), 0);
        hv_store(hv, "chatnet", 7, new_pv(conn->chatnet), 0);
@@ -273,6 +302,12 @@ void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn)
        hv_store(hv, "wanted_nick", 11, new_pv(conn->nick), 0);
        hv_store(hv, "username", 8, new_pv(conn->username), 0);
        hv_store(hv, "realname", 8, new_pv(conn->realname), 0);
+
+       hv_store(hv, "reconnection", 12, newSViv(conn->reconnection), 0);
+       hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0);
+       hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0);
+       hv_store(hv, "use_ssl", 7, newSViv(conn->use_ssl), 0);
+       hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0);
 }
 
 void perl_server_fill_hash(HV *hv, SERVER_REC *server)
@@ -401,6 +436,7 @@ void perl_nick_fill_hash(HV *hv, NICK_REC *nick)
        hv_store(hv, "op", 2, newSViv(nick->op), 0);
        hv_store(hv, "halfop", 6, newSViv(nick->halfop), 0);
        hv_store(hv, "voice", 5, newSViv(nick->voice), 0);
+       hv_store(hv, "other", 5, newSViv(nick->other), 0);
 
        hv_store(hv, "last_check", 10, newSViv(nick->last_check), 0);
        hv_store(hv, "send_massjoin", 13, newSViv(nick->send_massjoin), 0);
@@ -482,6 +518,24 @@ static void perl_reconnect_fill_hash(HV *hv, RECONNECT_REC *reconnect)
        hv_store(hv, "next_connect", 12, newSViv(reconnect->next_connect), 0);
 }
 
+static void perl_script_fill_hash(HV *hv, PERL_SCRIPT_REC *script)
+{
+       hv_store(hv, "name", 4, new_pv(script->name), 0);
+       hv_store(hv, "package", 7, new_pv(script->package), 0);
+       hv_store(hv, "path", 4, new_pv(script->path), 0);
+       hv_store(hv, "data", 4, new_pv(script->data), 0);
+}
+
+static void remove_newlines(char *str)
+{
+       char *writing = str;
+
+       for (;*str;str++)
+               if (*str != '\n' && *str != '\r')
+                       *(writing++) = *str;
+       *writing = '\0';
+}
+
 void perl_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item)
 {
         const char *cmdchars;
@@ -496,6 +550,14 @@ void perl_command(const char *cmd, SERVER_REC *server, WI_ITEM_REC *item)
                sendcmd = g_strdup_printf("%c%s", *cmdchars, cmd);
        }
 
+       /* remove \r and \n from commands,
+          to make it harder to introduce a security bug in a script */
+       if(strpbrk(sendcmd, "\r\n")) {
+               if (sendcmd == cmd)
+                       sendcmd = strdup(cmd);
+               remove_newlines(sendcmd);
+       }
+
        signal_emit("send command", 3, sendcmd, server, item);
        if (sendcmd != cmd) g_free(sendcmd);
 }
@@ -522,8 +584,13 @@ static void perl_register_protocol(CHAT_PROTOCOL_REC *rec)
        chat_type = chat_protocol_lookup(rec->name);
        g_return_if_fail(chat_type >= 0);
 
+#if GLIB_MAJOR_VERSION < 2
        name = g_strdup(rec->name);
        g_strdown(name+1);
+#else
+       name = g_ascii_strdown(rec->name,-1);
+       *name = *(rec->name);
+#endif
 
        /* window items: channel, query */
        type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL");
@@ -597,13 +664,14 @@ static int free_iobject_proto(void *key, void *value, void *chat_type)
 
 static void perl_unregister_protocol(CHAT_PROTOCOL_REC *rec)
 {
-        GSList *item;
+       GSList *item;
+       void *data;
 
        item = gslist_find_icase_string(use_protocols, rec->name);
        if (item != NULL) {
-               g_free(item->data);
-               use_protocols =
-                       g_slist_remove(use_protocols, item->data);
+               data = item->data;
+               use_protocols = g_slist_remove(use_protocols, data);
+               g_free(data);
        }
        g_hash_table_foreach_remove(iobject_stashes,
                                    (GHRFunc) free_iobject_proto,
@@ -619,6 +687,7 @@ void perl_common_start(void)
                { "Irssi::Logitem", (PERL_OBJECT_FUNC) perl_log_item_fill_hash },
                { "Irssi::Rawlog", (PERL_OBJECT_FUNC) perl_rawlog_fill_hash },
                { "Irssi::Reconnect", (PERL_OBJECT_FUNC) perl_reconnect_fill_hash },
+               { "Irssi::Script", (PERL_OBJECT_FUNC) perl_script_fill_hash },
 
                { NULL, NULL }
        };
index 2d300289cd60b8ebe237c2ce42ea0aba123c2647..2b660eefa813405aa73cb1538f8214180ccc70e4 100644 (file)
@@ -160,7 +160,12 @@ void perl_scripts_deinit(void)
 
        /* Unload all perl libraries loaded with dynaloader */
        perl_eval_pv("foreach my $lib (@DynaLoader::dl_modules) { if ($lib =~ /^Irssi\\b/) { $lib .= '::deinit();'; eval $lib; } }", TRUE);
-       perl_eval_pv("eval { foreach my $lib (@DynaLoader::dl_librefs) { DynaLoader::dl_unload_file($lib); } }", TRUE);
+
+       /* We could unload all libraries .. but this crashes with some
+          libraries, probably because we don't call some deinit function..
+          Anyway, this would free some memory with /SCRIPT RESET, but it
+          leaks memory anyway. */
+       /*perl_eval_pv("eval { foreach my $lib (@DynaLoader::dl_librefs) { DynaLoader::dl_unload_file($lib); } }", TRUE);*/
 
        /* perl interpreter */
        perl_destruct(my_perl);
@@ -368,11 +373,13 @@ char *perl_script_get_path(const char *name)
                /* check from SCRIPTDIR */
                g_free(path);
                path = g_strdup_printf(SCRIPTDIR"/%s", file);
-               if (stat(path, &statbuf) != 0)
-                        path = NULL;
+               if (stat(path, &statbuf) != 0) {
+                       g_free(path);
+                       path = NULL;
+               }
        }
        g_free(file);
-        return path;
+       return path;
 }
 
 /* If core should handle printing script errors */
@@ -463,8 +470,8 @@ void perl_core_init(void)
 
 void perl_core_deinit(void)
 {
-       perl_signals_deinit();
         perl_scripts_deinit();
+       perl_signals_deinit();
 
        signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
 }
index 3c47a6d7832119d9bc0ad0abe025e1c8fbe72b9d..7015cdf2a6b7b76bd83f601609e0434b00aa936d 100644 (file)
@@ -160,6 +160,25 @@ static void cmd_script_list(void)
                    TXT_SCRIPT_LIST_FOOTER);
 }
 
+static void cmd_load(const char *data, SERVER_REC *server, void *item)
+{
+        char *rootmodule, *submodule;
+       void *free_arg;
+       size_t len;
+
+       if (!cmd_get_params(data, &free_arg, 2 , &rootmodule, &submodule))
+               return;
+
+       len = strlen(rootmodule);
+       if (len > 3 && strcmp(rootmodule + len - 3, ".pl") == 0) {
+               /* make /LOAD script.pl work as expected */
+               signal_stop();
+               cmd_script_load(data);
+       }
+
+       cmd_params_free(free_arg);
+}
+
 static void sig_script_error(PERL_SCRIPT_REC *script, const char *error)
 {
        printformat(NULL, NULL, MSGLEVEL_CLIENTERROR,
@@ -230,6 +249,7 @@ void fe_perl_init(void)
        command_bind("script unload", NULL, (SIGNAL_FUNC) cmd_script_unload);
        command_bind("script reset", NULL, (SIGNAL_FUNC) cmd_script_reset);
        command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list);
+       command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
        command_set_options("script exec", "permanent");
 
         signal_add("script error", (SIGNAL_FUNC) sig_script_error);
@@ -248,6 +268,7 @@ void fe_perl_deinit(void)
        command_unbind("script unload", (SIGNAL_FUNC) cmd_script_unload);
        command_unbind("script reset", (SIGNAL_FUNC) cmd_script_reset);
        command_unbind("script list", (SIGNAL_FUNC) cmd_script_list);
+       command_unbind("load", (SIGNAL_FUNC) cmd_load);
 
         signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
        signal_remove("complete command script load", (SIGNAL_FUNC) sig_complete_load);
index 6dcde221143e936c86d2d99bd63c3ce3c3d88f0e..85e537de55e294ad030d8648f39b47662a761035 100644 (file)
@@ -39,6 +39,7 @@ typedef struct {
 typedef struct {
        char *signal;
        char *args[7];
+       int dynamic;
 } PERL_SIGNAL_ARGS_REC;
 
 #include "perl-signals-list.h"
@@ -388,6 +389,34 @@ void perl_signals_stop(void)
        signals = NULL;
 }
 
+static void register_signal_rec(PERL_SIGNAL_ARGS_REC *rec)
+{
+       if (rec->signal[strlen(rec->signal)-1] == ' ') {
+               perl_signal_args_partial =
+                       g_slist_append(perl_signal_args_partial, rec);
+       } else {
+               int signal_id = signal_get_uniq_id(rec->signal);
+               g_hash_table_insert(perl_signal_args_hash,
+                                   GINT_TO_POINTER(signal_id), rec);
+       }
+}
+
+void perl_signal_register(const char *signal, const char **args)
+{
+       PERL_SIGNAL_ARGS_REC *rec;
+       int i;
+
+       if (perl_signal_args_find(signal_get_uniq_id(signal)) != NULL)
+               return;
+
+       rec = g_new0(PERL_SIGNAL_ARGS_REC, 1);
+       for (i = 0; i < 6 && args[i] != NULL; i++)
+               rec->args[i] = g_strdup(args[i]);
+       rec->dynamic = TRUE;
+       rec->signal = g_strdup(signal);
+       register_signal_rec(rec);
+}
+
 void perl_signals_init(void)
 {
        int n;
@@ -396,23 +425,35 @@ void perl_signals_init(void)
                                                 (GCompareFunc) g_direct_equal);
         perl_signal_args_partial = NULL;
 
-       for (n = 0; perl_signal_args[n].signal != NULL; n++) {
-               PERL_SIGNAL_ARGS_REC *rec = &perl_signal_args[n];
+       for (n = 0; perl_signal_args[n].signal != NULL; n++)
+               register_signal_rec(&perl_signal_args[n]);
+}
 
-               if (rec->signal[strlen(rec->signal)-1] == ' ') {
-                       perl_signal_args_partial =
-                               g_slist_append(perl_signal_args_partial, rec);
-               } else {
-                        int signal_id = signal_get_uniq_id(rec->signal);
-                       g_hash_table_insert(perl_signal_args_hash,
-                                           GINT_TO_POINTER(signal_id),
-                                           rec);
-               }
-       }
+static void signal_args_free(PERL_SIGNAL_ARGS_REC *rec)
+{
+       int i;
+
+       if (!rec->dynamic)
+               return;
+
+       for (i = 0; i < 6 && rec->args[i] != NULL; i++)
+               g_free(rec->args[i]);
+       g_free(rec->signal);
+       g_free(rec);
+}
+
+static void signal_args_hash_free(void *key, PERL_SIGNAL_ARGS_REC *rec)
+{
+        signal_args_free(rec);
 }
 
 void perl_signals_deinit(void)
 {
-        g_slist_free(perl_signal_args_partial);
+       g_slist_foreach(perl_signal_args_partial,
+                       (GFunc) signal_args_free, NULL);
+       g_slist_free(perl_signal_args_partial);
+
+       g_hash_table_foreach(perl_signal_args_hash,
+                            (GHFunc) signal_args_hash_free, NULL);
         g_hash_table_destroy(perl_signal_args_hash);
 }
index 60e6636e279ab24d0bc68bae5f599e90dc416214..f0b8e44221f8e836d2041500e72bb0618da3291a 100644 (file)
@@ -21,6 +21,8 @@ void perl_command_unbind(const char *cmd, SV *func);
 void perl_command_runsub(const char *cmd, const char *data, 
                         SERVER_REC *server, WI_ITEM_REC *item);
 
+void perl_signal_register(const char *signal, const char **args);
+
 void perl_signals_start(void);
 void perl_signals_stop(void);
 
index 4fb92f11a6d00ba9bfa5eca44ffb297536962852..91a77a8026b1123b848888c8c9d08ea93994d2ca 100644 (file)
@@ -1,4 +1,5 @@
 #include "module.h"
+#include "recode.h"
 
 MODULE = Irssi::TextUI::TextBuffer  PACKAGE = Irssi
 PROTOTYPES: ENABLE
@@ -75,9 +76,13 @@ textbuffer_line_get_text(line, coloring)
        int coloring
 PREINIT:
        GString *str;
+       SV *result;
 PPCODE:
        str = g_string_new(NULL);
        textbuffer_line2text(line, coloring, str);
-       XPUSHs(sv_2mortal(new_pv(str->str)));
+       result = new_pv(str->str);
+       if (is_utf8())
+               SvUTF8_on(result);
+       XPUSHs(sv_2mortal(result));
        g_string_free(str, TRUE);
 
index 4f23a9a0507a09667dc776227148914e4b72bf02..a958e2020597e631b55cfc02d9524af6719f6fab 100644 (file)
@@ -107,6 +107,7 @@ deinit()
 CODE:
        if (!initialized) return;
         perl_statusbar_deinit();
+       initialized = FALSE;
 
 MODULE = Irssi::TextUI PACKAGE = Irssi
 
index 11a2951c32832d550f9701435d7bf5e51881fc1c..7ff31aac5868e6aafd20b41da60faa9d8ac2b150 100644 (file)
@@ -3,9 +3,11 @@
 static int magic_free_text_dest(pTHX_ SV *sv, MAGIC *mg)
 {
        TEXT_DEST_REC *dest = (TEXT_DEST_REC *) mg->mg_ptr;
-       g_free((char *) dest->target);
+       char *target = (char *) dest->target;
+       g_free(target);
        g_free(dest);
        mg->mg_ptr = NULL;
+       sv_setiv(sv, 0);
        return 0;
 }
 
@@ -131,4 +133,4 @@ print(dest, str)
        Irssi::UI::TextDest dest
        char *str
 CODE:
-       printtext_dest(dest, str);
+       printtext_dest(dest, "%s", str);
index 7f1c5277f14470e74be46f9fd68316b93828001f..4afd437c4dc92f03c466715b7b08e6e3169deb40 100644 (file)
@@ -136,6 +136,32 @@ CODE:
 
         printformat_perl(&dest, format, arglist);
 
+void
+abstracts_register(abstracts)
+       SV *abstracts
+PREINIT:
+       AV *av;
+       char *key, *value;
+       int i, len;
+CODE:
+        if (!SvROK(abstracts))
+               croak("abstracts is not a reference to list");
+       av = (AV *) SvRV(abstracts);
+       len = av_len(av)+1;
+       if (len == 0 || (len & 1) != 0)
+               croak("abstracts list is invalid - not divisible by 2 (%d)", len);
+
+        for (i = 0; i < len; i++) {
+               key = SvPV(*av_fetch(av, i, 0), PL_na); i++;
+               value = SvPV(*av_fetch(av, i, 0), PL_na);
+
+               theme_set_default_abstract(key, value);
+       }
+       themes_reload();
+
+void
+themes_reload()
+
 #*******************************
 MODULE = Irssi::UI::Themes  PACKAGE = Irssi::Server
 #*******************************
index 670fb1a598115cbac55422be890314e4f004d670..d8c7f7aef4d440b7bc90f9f85ad158e1b5624f42 100644 (file)
@@ -100,6 +100,7 @@ deinit()
 CODE:
        if (!initialized) return;
         perl_themes_deinit();
+       initialized = FALSE;
 
 BOOT:
        irssi_boot(UI__Formats);
index f8ef0315a7533798143eeee5ba4c09b249b50e69..ab52c910913a90f643db63d47b7ef4486922800b 100644 (file)
@@ -160,7 +160,8 @@ CODE:
        old = active_win;
        active_win = window;
        perl_command(cmd, window->active_server, window->active);
-        if (g_slist_find(windows, old) != NULL)
+       if (active_win == window &&
+           g_slist_find(windows, old) != NULL)
                active_win = old;
 
 void
index 455aeed30d6426813eef96d0337536714f9268f0..d661187088046b4efe3566e2d0695dec40d69bb5 100644 (file)
@@ -1305,7 +1305,7 @@ void silc_server_free_ftp(SILC_SERVER_REC *server,
 bool silc_term_utf8(void)
 {
   const char *str;
-  str = settings_get_str("term_type");
+  str = settings_get_str("term_charset");
   if (str)
     if (g_strcasecmp(str, "utf-8") == 0)
       return TRUE;