+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.
-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
+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...
+ - /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
#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
+
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
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
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 **
])
fi
+if test "x$want_cuix" = "xyes"; then
+ AC_DEFINE([HAVE_CUIX], [1] ,[Enable cuix support])
+fi
+
dnl **
dnl ** fe-text checks
dnl **
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
src/perl/textui/Makefile.PL
src/perl/silc/Makefile.PL
scripts/Makefile
+scripts/examples/Makefile
docs/Makefile
docs/help/Makefile
docs/help/in/Makefile
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?
--- /dev/null
+<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 <tab></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>
+
# 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@
--- /dev/null
+
+@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
+
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>
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)
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.
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)
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
"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
--------
"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
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
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
$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)
--- /dev/null
+ <h2>Startup HOWTO</h2>
+
+ <h3>îÏ×ÉÞËÁÍ × Irssi (Á ÎÅ IRC ..)</h3>
+
+ <p>© 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 & ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ</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 <ÎÏÍÅÒ> - ðÅÒÅËÌÀÞÅÎÉÅ ÎÁ ÏËÎÏ Ó ÚÁÄÁÎÎÙÍ ÎÏÍÅÒÏÍ
+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 [<number>|<name>] - óÄÅÌÁÔØ ÒÁÚÄÅÌÅÎÎÏÅ ÏËÎÏ ÓËÒÙÔÙÍ
+/WINDOW SHOW <number>|<name> - óÄÅÌÁÔØ ÓËÒÙÔÏÅ ÏËÎÏ ÒÁÚÄÅÌÅÎÎÙÍ
+
+/WINDOW SHRINK [<lines>] - õÍÅÎØÛÉÔØ ÁËÔÉ×ÎÏÅ ÏËÎÏ
+/WINDOW GROW [<lines>] - õ×ÅÌÉÞÉÔØ ÁËÔÉ×ÎÏÅ ÏËÎÏ
+/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 <ÎÏÍÅÒ>|<ÉÍÑ> - ÐÅÒÅÍÅÓÔÉÔØ ËÁÎÁÌ ÉÌÉ ÐÒÉ×ÁÔ × ÄÒÕÇÏÅ ÏËÎÏ
+</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 & ÕÒÏ×ÎÉ ÓÏÏÂÝÅÎÉÊ</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/<ÍÅÔËÁ_ÓÅÒ×ÅÒÁ>/<ÃÅÌØ>.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 <áÄÒÅÓ ÐÒÏËÓÉ>
+/SET proxy_port <ðÏÒÔ>
+</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 <ÐÁÒÏÌØ>
+/SET irssiproxy_ports <IRC_ÓÅÔØ>=<ÐÏÒÔ> ... (ÎÁÐÒÉÍÅÒ 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 <ÐÁÒÏÌØ>
+</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><ÎÉË> ÔÅËÓÔ</code>
+ Á ÔÅ, ÞÔÏ ÐÒÉÈÏÄÑÔ ÎÁ ÄÒÕÇÉÅ ËÁÎÁÌÙ ÔÁË: <code><ÎÉË:ËÁÎÁÌ> ÔÅËÓÔ</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><@nick></code> Õ ÏÐÏ×, <code><+nick></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 <ÉÍÑ></code> ×Ù×ÏÄÉÔ ÎÁÓÔÒÏÊËÉ ÐÁÎÅÌÉ ÓÔÁÔÕÓÁ É Å£ ËÏÍÐÏÎÅÎÔÙ.
+<code>/STATUSBAR <ÉÍÑ> ENABLE|DISABLE</code>
+×ËÌÀÞÁÅÔ ÉÌÉ ÏÔËÌÀÞÁÅÔ ÐÁÎÅÌØ. <code>/STATUSBAR <ÉÍÑ> RESET</code>
+ÕÓÔÁÎÁ×ÌÉ×ÁÅÔ ÄÌÑ ÐÁÎÅÌÉ ÓÔÁÔÕÓÁ ÎÁÓÔÒÏÊËÉ ÐÏ ÕÍÏÌÞÁÎÉÀ, ÉÌÉ ÅÓÌÉ ÏÎÁ ÂÙÌÁ ÓÏÚÄÁÎÁ ×ÁÍÉ, ÔÏ ÕÄÁÌÑÅÔ Å£.</p>
+
+<p>ðÁÎÅÌØ ÍÏÖÅÔ ÉÍÅÔØ Ä×Á ÔÉÐÁ: windows É root - ÜÔÏ ÐÏÄÒÁÚÕÍÅ×ÁÅÔ, ÞÔÏ ÏÎÁ ÍÏÖÅÔ ÂÙÔØ ×ÉÄÎÁ ÄÌÑ ×ÓÅÈ ÏËÏÎ ÉÌÉ ÔÏÌØËÏ ÄÌÑ ÏÄÎÏÇÏ.
+Placement - ÜÔÏ ÒÁÓÐÏÌÏÖÅÎÉÅ ÐÁÎÅÌÉ: top - Ó×ÅÒÈÕ, bottom - ÓÎÉÚÕ.
+Position - ÜÔÏ ÞÉÓÌÏ, ÞÅÍ ÂÏÌØÛÅ ÚÎÁÞÅÎÉÅ ËÏÔÏÒÏÇÏ, ÔÅÍ ÎÉÖÅ ÎÁ ÜËÒÁÎÅ ÒÁÓÐÏÌÁÇÁÅÔÓÑ ÐÁÎÅÌØ.
+ðÁÒÁÍÅÔÒ Visible ÍÏÖÅÔ ÐÒÉÎÉÍÁÔØ 3 ÚÎÁÞÅÎÉÑ: always, active É inactive. òÅÖÉÍÙ active/inactive ÐÏÌÅÚÎÙ ÔÏÌØËÏ ÄÌÑ ÒÁÚÄÅÌÅÎÎÙÈ ÏËÏÎ.
+üÔÉ ÎÁÓÔÒÏÊËÉ ÍÏÇÕÔ ÂÙÔØ ÉÚÍÅÎÅÎÙ ÓÌÅÄÕÀÝÉÍÉ ËÏÍÁÎÄÁÍÉ:</p>
+
+<pre>
+/STATUSBAR <ÉÍÑ> TYPE window|root
+/STATUSBAR <ÉÍÑ> PLACEMENT top|bottom
+/STATUSBAR <ÉÍÑ> POSITION <num>
+/STATUSBAR <ÉÍÑ> VISIBLE always|active|inactive
+</pre>
+
+<p>ëÏÇÄÁ ×Ù ÚÁÇÒÕÖÁÅÔÅ ÎÏ×ÙÅ ÓËÒÉÐÔÙ ÄÌÑ ÐÁÎÅÌÅÊ ÓÔÁÔÕÓÁ ×ÁÍ ÓËÏÒÅÅ ×ÓÅÇÏ ÐÒÉÄÅÔÓÑ ×ÙÂÒÁÔØ ÇÄÅ ×Ù ÈÏÔÉÔÅ ÉÈ ÒÁÓÐÏÌÏÖÉÔØ.
+ëÏÍÐÏÎÅÎÔÙ ÐÁÎÅÌÅÊ ÍÏÇÕÔ ÂÙÔØ ÉÚÍÅÎÅÎÙ ÓÌÅÄÕÀÝÉÍÉ ËÏÍÁÎÄÁÍÉ:</p>
+
+<pre>
+/STATUSBAR <ÉÍÑ> ADD [-before | -after <item>] [-priority #] [-alignment left|right] <ËÏÍÐÏÎÅÎÔÁ(item)>
+/STATUSBAR <ÉÍÑ> REMOVE <ËÏÍÐÏÎÅÎÔÁ(item)>
+</pre>
+
+<p>ïÂÙÞÎÏ ÄÌÑ ÉÍÑ ËÏÍÐÏÎÅÎÔÙ × ÓËÒÉÐÔÅ ÄÌÑ ÐÁÎÅÌÉ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÉÍÅÎÉ ÓËÒÉÐÔÁ.
+ï ÜÔÏÍ ÄÏÌÖÎÏ ÂÙÔØ ÎÁÐÉÓÁÎÏ × ÄÏËÕÍÅÎÔÁÃÉÉ Ë ÓËÒÉÐÔÕ. ôÁË ÞÔÏ ÞÔÏÂÙ ÄÏÂÁ×ÉÔØ ÓËÒÉÐÔ mail.pl
+ÐÅÒÅÄ ÓÐÉÓËÏÍ ÁËÔÉ×ÎÙÈ ÏËÏÎ (ÓÍÏÔÒÉÔÅ
+<code>/STATUSBAR</code>), ××ÅÄÉÔÅ ÜÔÕ ËÏÍÁÎÄÕ: <code>/STATUSBAR window ADD -before
+act mail</code>.</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>
</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
<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>
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
<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
<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
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
/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>
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
<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>
<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
/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
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 <password>
-/SET irssiproxy_ports <ircnet>=<port> ... (eg. ircnet=2777 efnet=2778)
+/SET irssiproxy_ports <network>=<port> ... (eg. IRCnet=2777 efnet=2778)
</pre>
<p><strong>NOTE</strong>: you <strong>MUST</strong> add all the servers you
-are using to server and ircnet lists with <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>
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>
<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
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
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"
;;
:
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([
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
-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)
# /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);
if (!$temp_opped{$nick} &&
$server->masks_match($masks, $nick, $host)) {
- $channel->command("/op $nick");
+ $channel->command("op $nick");
$temp_opped{$nick} = 1;
}
}
--- /dev/null
+# 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');
--- /dev/null
+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!!');
+}
# /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
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;
# 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};
--- /dev/null
+scriptdir = $(datadir)/irssi/scripts
+
+script_DATA = \
+ command.pl \
+ msg-event.pl \
+ redirect.pl
+
+EXTRA_DIST = $(script_DATA)
--- /dev/null
+# 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');
--- /dev/null
+# 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');
--- /dev/null
+# 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');
--- /dev/null
+# 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');
+$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);
}
}
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
--- /dev/null
+# /MLOCK <channel> <mode>
+#
+# Locks the channel mode to <mode>, if someone else tries to change the mode
+# Irssi will automatically change it back. +k and +l are a bit special since
+# they require the parameter. If you omit the parameter, like setting the
+# mode to "+ntlk", Irssi will allow all +k and +l (or -lk) mode changes.
+# 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");
--- /dev/null
+# If quit message isn't given, quit with a random message
+# read from ~/.irssi/irssi.quit
+
+use Irssi;
+use Irssi::Irc;
+use strict;
+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');
--- /dev/null
+# 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';
--- /dev/null
+# /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');
--- /dev/null
+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');
+
PERLDIR=perl
endif
+pkginc_srcdir=$(pkgincludedir)/src
+pkginc_src_HEADERS = \
+ common.h
noinst_HEADERS = \
common.h
# 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 */
pidwait.c \
queries.c \
rawlog.c \
+ recode.c \
servers.c \
servers-reconnect.c \
servers-setup.c \
pidwait.h \
queries.h \
rawlog.h \
+ recode.h \
servers.h \
servers-reconnect.h \
servers-setup.h \
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);
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) ||
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)) {
#include "commands.h"
#include "special-vars.h"
#include "settings.h"
+#include "recode.h"
#include "chat-protocols.h"
#include "servers.h"
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);
/* 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)
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)
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;
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;
signal_emit("gui exit", 0);
}
+/* SYNTAX: JOIN [-invite] [-<server tag>] <channels> [<keys>] */
static void cmd_join(const char *data, SERVER_REC *server)
{
GHashTable *optlist;
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);
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)
{
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");
}
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);
}
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);
if (cb->func == func) {
rec->callbacks =
g_slist_remove(rec->callbacks, cb);
+ g_free(cb);
return rec;
}
}
}
(*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 = "-";
*optlist[pos] == '!')
option = NULL;
- while (i_isspace(**data)) (*data)++;
+ while (**data == ' ') (*data)++;
continue;
}
}
option = NULL;
- while (i_isspace(**data)) (*data)++;
+ while (**data == ' ') (*data)++;
}
return 0;
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> */
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' */
#include "log.h"
#include "rawlog.h"
#include "ignore.h"
+#include "recode.h"
#include "channels.h"
#include "queries.h"
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;
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;
{
dialog_type_queue = NULL;
dialog_text_queue = NULL;
+ client_start_time = time(NULL);
modules_init();
#ifndef WIN32
log_init();
log_away_init();
rawlog_init();
+ recode_init();
channels_init();
queries_init();
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);
queries_deinit();
channels_deinit();
+ recode_deinit();
rawlog_deinit();
log_away_deinit();
log_deinit();
#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
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[]);
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include "core.h"
#include "module.h"
#include "modules.h"
#include "signals.h"
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;
/* 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');
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);
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)
{
/* 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 */
#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;
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);
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);
}
}
+ ret = remove_newline(rec);
*output = rec->str;
- return remove_newline(rec);
+ return ret;
}
void line_split_free(LINEBUF_REC *buffer)
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)
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);
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;
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)
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;
+}
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
/* 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);
/* 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
GModule *gmodule;
MODULE_REC *module;
MODULE_FILE_REC *rec;
+ gpointer value1, value2;
char *initfunc, *deinitfunc;
int found;
/* 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);
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);
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);
static int g_io_channel_write_block(GIOChannel *channel, void *data, int len)
{
- unsigned int ret;
+ gsize ret;
int err, sent;
sent = 0;
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;
#endif
/* child */
+ srand(time(NULL));
+
memset(&rec, 0, sizeof(rec));
rec.error = net_gethostbyname(addr, &rec.ip4, &rec.ip6);
if (rec.error == 0) {
#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
#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);
#include "module.h"
#include "network.h"
+#include "misc.h"
#ifdef HAVE_OPENSSL
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)
{
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)
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)
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)
#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)
{
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)
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)
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)
#endif
-static SSL_CTX *ssl_ctx = NULL;
-
static gboolean irssi_ssl_init(void)
{
SSL_library_init();
}
-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);
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;
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;
}
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;
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;
# define INADDR_NONE INADDR_BROADCAST
#endif
-union irssi_sockaddr_union {
+union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_IPV6
/* 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)
}
-/* 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
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;
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)
/* 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) {
/* 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;
}
}
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);
/* 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;
/* 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);
/* 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);
/* 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);
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);
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
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
*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);
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
#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 */
unsigned int op:1;
unsigned int halfop:1;
unsigned int voice:1;
+char other;
/*GHashTable *module_data;*/
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;
* 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;
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;
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);
--- /dev/null
+/*
+ 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)
+{
+
+}
--- /dev/null
+#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 */
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;
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);
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 */
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;
rec->next_connect = next_connect;
rec->conn = conn;
+ conn->reconnecting = TRUE;
server_connect_ref(conn);
reconnects = g_slist_append(reconnects, rec);
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;
}
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)
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;
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);
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);
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",
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);
}
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);
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 :
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);
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 =
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);
#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)
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));
}
{
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());
/* 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);
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);
}
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);
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);
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);
{
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);
#ifndef __SESSION_H
#define __SESSION_H
+extern char *irssi_binary;
+
void session_set_binary(const char *path);
void session_upgrade(void);
#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;
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;
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;
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;
}
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);
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;
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);
}
}
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
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);
"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++;
config = config_open(NULL, -1);
}
- if (path != NULL)
+ if (config->fname != NULL)
config_parse(config);
else
config_parse_data(config, default_config, "internal");
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);
}
#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 */
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,
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);
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 */
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);
}
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);
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 \
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 \
}
}
+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 */
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;
*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);
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);
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)
}
}
-
static void sig_complete_channel(GList **list, WINDOW_REC *window,
const char *word, const char *line,
int *want_space)
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)
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);
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);
((c) == ',')
#define isseparator(c) \
- (i_isspace(c) || isseparator_notspace(c))
+ ((c) == ' ' || isseparator_notspace(c))
void chat_completion_init(void);
void chat_completion_deinit(void);
} else {
checkcmd = g_strndup(line, (int) (ptr-line));
- while (i_isspace(*ptr)) ptr++;
+ while (*ptr == ' ') ptr++;
cmdargs = ptr;
}
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;
"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);
{
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)
}
/* 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;
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;
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] = '%';
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;
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,
#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"
#include "window-activity.h"
#include "window-items.h"
#include "windows-layout.h"
+#include "fe-recode.h"
#include <signal.h>
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",
static void sig_disconnected(SERVER_REC *server)
{
- g_free(MODULE_DATA(server));
+ void *data = MODULE_DATA(server);
+ g_free(data);
MODULE_DATA_UNSET(server);
}
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);
}
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 },
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);
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);
fe_messages_init();
hilight_text_init();
fe_ignore_messages_init();
+ fe_recode_init();
settings_check();
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);
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) {
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;
#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;
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);
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"
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;
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();
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);
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);
void fe_core_commands_init(void)
{
- command_hide_output = FALSE;
+ command_hide_output = 0;
command_cmd = FALSE;
memset(&time_command_now, 0, sizeof(GTimeVal));
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);
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);
#include "misc.h"
#include "levels.h"
+#include "servers.h"
#include "channels.h"
#include "queries.h"
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);
}
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);
}
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;
}
target = NULL;
+ target_server = NULL;
notice = FALSE;
if (g_hash_table_lookup(optlist, "in") != NULL) {
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;
}
/* 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;
}
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;
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);
}
}
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 " : "",
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 */
ptr = cmd+strlen(cmd);
while (ptr[-1] == ' ') ptr--; *ptr = '\0';
+ g_strdown(cmd);
show_help(cmd);
g_free(cmd);
}
}
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);
}
/* 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();
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;
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);
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 */
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)
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"))
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);
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();
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;
#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;
/* 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;
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* */
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 ? "%" :
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 */
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);
print_channel = TRUE;
level = MSGLEVEL_PUBLIC;
- if (for_me || color != NULL)
+ if (for_me)
level |= MSGLEVEL_HILIGHT;
if (settings_get_bool("emphasis"))
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);
}
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;
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);
}
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
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);
}
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))
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++;
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);
}
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;
list[count++] = name;
list[count++] = g_strconcat("fe_", name, NULL);
+ list[count++] = g_strconcat("fe_common_", name, NULL);
}
list[count] = NULL;
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,
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) {
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();
--- /dev/null
+/*
+ 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
+}
--- /dev/null
+#ifndef __FE_RECODE_H
+#define __FE_RECODE_H
+
+void fe_recode_init (void);
+void fe_recode_deinit (void);
+
+#endif /* __FE_RECODE_H */
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;
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);
}
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);
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:
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;
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);
}
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));
/* 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;
}
}
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);
#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";
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 == '+')
(*format)++;
}
g_string_append_c(out, ',');
- (*format)--;
+ (*format)--;
break;
case 's':
case 'S':
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)++;
{
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':
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;
break;
case '[':
/* code */
- format_expand_code(format, out, flags);
+ format_expand_code(format, out, flags);
break;
default:
/* check if it's a background color */
{
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:
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%ld", l);
- bufpos += len+1;
+ bufpos += len+1;
break;
}
case FORMAT_FLOAT: {
}
}
}
-
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;
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;
}
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'
{
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)
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);
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;
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);
}
ret = str->str;
g_string_free(str, FALSE);
- return ret;
+ return ret;
}
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 \
{
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)
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) {
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);
}
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;
}
if (count < 2)
- return NULL;
+ return NULL;
}
return format_get_text_theme(theme, MODULE_NAME, dest,
/* foreground color */
if (**str != ',') {
fg = **str-'0';
- (*str)++;
+ (*str)++;
if (i_isdigit(**str)) {
fg = fg*10 + (**str-'0');
(*str)++;
{
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;
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)
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;
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),
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';
flags &= ~GUI_PRINT_FLAG_BLINK;
else {
/* blink */
- bgcolor -= 8;
- flags |= GUI_PRINT_FLAG_BLINK;
+ bgcolor -= 8;
+ flags |= GUI_PRINT_FLAG_BLINK;
}
}
}
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");
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);
settings_get_str("hilight_act_color"));
}
-static char *hilight_get_color(HILIGHT_REC *rec)
+char *hilight_get_color(HILIGHT_REC *rec)
{
const char *color;
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;
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)
{
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)
#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)
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 : "",
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();
# include <regex.h>
#endif
+#include "formats.h"
+
typedef struct _HILIGHT_REC HILIGHT_REC;
struct _HILIGHT_REC {
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);
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;
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;
/* 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 */
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,
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,
{ "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 } },
{ "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 },
{ "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 },
TXT_LOOKING_UP,
TXT_CONNECTING,
- TXT_CONNECTION_ESTABLISHED,
+ TXT_RECONNECTING,
+ TXT_CONNECTION_ESTABLISHED,
TXT_CANT_CONNECT,
TXT_CONNECTION_LOST,
TXT_LAG_DISCONNECTED,
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,
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,
#define MODULE_NAME "fe-common/core"
+typedef guint32 unichar;
typedef struct {
time_t time;
char *nick;
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);
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");
}
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)
{
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 == '}')
continue;
}
- if (!braces) {
+ if (braces == 0) {
(*format)++;
break;
}
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;
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;
if (oldtheme != NULL && theme != NULL) {
theme_destroy(oldtheme);
+ if (current_theme == oldtheme)
+ current_theme = theme;
window_themes_update();
}
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;
}
}
-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",
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);
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;
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;
change_theme(theme, TRUE);
}
-static void themes_read(void)
+void themes_reload(void)
{
GSList *refs;
char *fname;
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);
}
}
}
+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");
{
while (themes != NULL)
theme_destroy(themes->data);
+ theme_destroy(internal_theme);
g_hash_table_destroy(default_formats);
default_formats = NULL;
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);
}
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
char *save_last_fg, char *save_last_bg,
int flags);
+void themes_reload(void);
+
void themes_init(void);
void themes_deinit(void);
(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;
}
{
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++;
}
*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,
--- /dev/null
+#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
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))
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);
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);
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;
{
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> */
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);
curses_sources = \
term-curses.c
+cuix_sources = \
+ cuix-api.c \
+ cuix-lib.c \
+ cuix.c
+
if NEED_TPARM
use_tparm_sources = $(tparm_sources)
endif
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 \
textbuffer-commands.c \
textbuffer-reformat.c \
textbuffer-view.c \
- utf8.c \
silc.c \
module-formats.c
textbuffer.h \
textbuffer-view.h \
textbuffer-reformat.h \
- utf8.h \
module.h \
module-formats.h
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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 */
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef __CUIX_LIB_H
+#define __CUIX_LIB_H
+
+#include "cuix-api.h"
+
+int home_menu (char *);
+
+
+#endif /* __CUIX_LIB_H */
--- /dev/null
+#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 ();
+}
+
+
--- /dev/null
+#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 */
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)
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;
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;
}
/* 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) {
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;
}
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 */
(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);
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);
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);
{
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);
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);
#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;
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 */
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);
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;
}
}
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)
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");
}
(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);
*/
#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"
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);
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;
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);
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 */
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';
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);
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) {
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) {
time_t get_idle_time(void)
{
- return idle_time;
+ return last_keypress.tv_sec;
}
static void key_scroll_backward(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 = word_complete(active_win, text, &pos, erase);
g_free(text);
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);
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";
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();
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);
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);
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)
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);
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);
}
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;
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)
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);
{ "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 }
};
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];
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;
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
}
#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[] = {
{ 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);
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);
/* 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();
}
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;
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");
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);
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)
#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;
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 {
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;
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);
}
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();
}
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;
--- /dev/null
+#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;
+};
/* bold */
if (col & 0x08)
col |= ATTR_BOLD;
- else if (col & ATTR_BOLD)
+ if (col & ATTR_BOLD)
terminfo_set_bold();
/* underline */
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;
vcx = term_width-1;
if (vcy >= term_height)
vcy = term_height-1;
+ }
}
static void term_printed_text(int count)
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;
}
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)
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();
}
{
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:
}
}
-/* 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)) {
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;
}
}
term_resize_dirty();
}
+static void cmd_redraw(void)
+{
+ irssi_redraw();
+}
+
static void read_settings(void)
{
const char *str;
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)
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");
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);
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);
}
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]);
if (tgetent(term->buffer1, term_env) < 1)
{
fprintf(term->out, "Termcap not found for TERM=%s\n", term_env);
- return -1;
+ return 0;
}
#endif
#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);
"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);
}
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)
}
}
+#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);
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);
}
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);
}
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');
#include "module.h"
#include "textbuffer-view.h"
#include "utf8.h"
+#ifdef HAVE_CUIX
+#include "cuix.h"
+#endif
typedef struct {
char *name;
}
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);
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;
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);
}
}
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;
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);
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,
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)
{
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 */
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 (*) ());
}
}
}
+++ /dev/null
-#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
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;
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
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
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
#include "core.h"
#include "pidwait.h"
+#include "session.h"
#define DEFAULT_COMMAND_CATEGORY "Perl scripts' commands"
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;
}
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;
}
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:
OUTPUT:
RETVAL
+char *
+get_irssi_binary()
+CODE:
+ RETVAL = irssi_binary;
+OUTPUT:
+ RETVAL
+
char *
version()
PREINIT:
if (!initialized) return;
perl_expando_deinit();
perl_settings_deinit();
+ initialized = FALSE;
BOOT:
irssi_boot(Channel);
Irssi::Server server
char flag
CODE:
- RETVAL = server->isnickflag(flag);
+ RETVAL = server->isnickflag(server, flag);
OUTPUT:
RETVAL
get_nick_flags(server)
Irssi::Server server
CODE:
- RETVAL = (char *) server->get_nick_flags();
+ RETVAL = (char *) server->get_nick_flags(server);
OUTPUT:
RETVAL
#include "module.h"
#include "misc.h"
+#include "recode.h"
static GHashTable *perl_settings;
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
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
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
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
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";
}
#include "queries.h"
#include "nicklist.h"
+#include "perl-core.h"
#include "perl-common.h"
typedef struct {
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;
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);
}
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));
{
SV **sv;
HV *hv;
+ void *p;
hv = hvref(o);
if (hv == NULL)
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,
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);
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)
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);
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;
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);
}
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");
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,
{ "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 }
};
/* 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);
/* 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 */
void perl_core_deinit(void)
{
- perl_signals_deinit();
perl_scripts_deinit();
+ perl_signals_deinit();
signal_remove("script error", (SIGNAL_FUNC) sig_script_error);
}
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,
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);
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);
typedef struct {
char *signal;
char *args[7];
+ int dynamic;
} PERL_SIGNAL_ARGS_REC;
#include "perl-signals-list.h"
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;
(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);
}
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);
#include "module.h"
+#include "recode.h"
MODULE = Irssi::TextUI::TextBuffer PACKAGE = Irssi
PROTOTYPES: ENABLE
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);
CODE:
if (!initialized) return;
perl_statusbar_deinit();
+ initialized = FALSE;
MODULE = Irssi::TextUI PACKAGE = Irssi
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;
}
Irssi::UI::TextDest dest
char *str
CODE:
- printtext_dest(dest, str);
+ printtext_dest(dest, "%s", str);
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
#*******************************
CODE:
if (!initialized) return;
perl_themes_deinit();
+ initialized = FALSE;
BOOT:
irssi_boot(UI__Formats);
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
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;