1
0
mirror of https://github.com/irssi/irssi.git synced 2024-09-01 04:14:16 -04:00

Merge branch 'master' into integrate/0.8.20

This commit is contained in:
ailin-nemui 2016-09-21 15:59:33 +02:00
commit ac73255483
101 changed files with 1619 additions and 2613 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
# On Windows, some Git clients may normalize all text files' line-endings to
# CRLF, which causes obscure errors when running shell scripts.
*.sh eol=lf

View File

@ -16,11 +16,31 @@ addons:
before_install:
- perl -V
- ./autogen.sh --with-proxy --with-bot --with-perl=module
- make dist
- cd ..
- tar xaf */irssi-*.tar.*
- cd irssi-*
install: true
script:
- ./autogen.sh --with-proxy --with-bot --with-perl=module --prefix=$HOME/irssi-build
- cat config.log
install:
- ./configure --with-proxy --with-bot --with-perl=module --prefix=$HOME/irssi-build
- make CFLAGS="-Wall -Werror"
- make install
before_script:
- cd
- mkdir irssi-test
- echo echo automated irssi launch test > irssi-test/startup;
echo ^set settings_autosave off >> irssi-test/startup;
echo ^set -clear log_close_string >> irssi-test/startup;
echo ^set -clear log_day_changed >> irssi-test/startup;
echo ^set -clear log_open_string >> irssi-test/startup;
echo ^set log_timestamp '* ' >> irssi-test/startup;
echo ^window log on >> irssi-test/startup
- echo load perl >> irssi-test/startup
- echo load proxy >> irssi-test/startup
- echo ^quit >> irssi-test/startup
- irssi-build/bin/irssi --home irssi-test
- cat irc.log.*
script: true

View File

@ -97,3 +97,5 @@ Other patches (grep for "patch" in ChangeLog) by:
Paul Johnson
KindOne
Fabian Kurz
Todd Pratt
xavierog

26
INSTALL
View File

@ -11,7 +11,8 @@ To compile irssi you need:
For most people, this should work just fine:
./configure
./autogen.sh (for people who just cloned the repository)
./configure (if this script already exists, skip ./autogen.sh)
make
su
make install (not _really_ required except for perl support)
@ -28,10 +29,6 @@ configure options
Build the irssi proxy (see startup-HOWTO).
--disable-ipv6
Disable IPv6 support.
--disable-ssl
Disable SSL support.
@ -62,17 +59,11 @@ configure options
Build without text frontend
If ncurses is installed in a non-standard path you can specify it with
--with-ncurses=/path. If anything else is in non-standard path, you can just
give the paths in CPPFLAGS and LIBS environment variable, eg.:
If anything is in non-standard path, you can just give the paths in
CPPFLAGS and LIBS environment variable, eg.:
CPPFLAGS=-I/opt/openssl/include LDFLAGS=-L/opt/openssl/lib ./configure
Irssi doesn't really need curses anymore, by default it uses
terminfo/termcap directly. The functions for using terminfo/termcap
however are usually only in curses library, some systems use libtermcap
as well. If you want to use only curses calls for some reason, use
--without-terminfo.
Perl problems
@ -94,10 +85,11 @@ things that can go wrong:
- If configure complains that it doesn't find some perl stuff, you're
probably missing libperl.so or libperl.a. In debian, you'll need to do
apt-get install libperl-dev
- For unprivileged home directory installations, using the local::lib CPAN
module is recommended. Read its docs, bootstrap it if needed, ensure that
the environment variables are set before running the configure script, and
append "--with-perl-lib=site" to the configure parameters to use it.
- For unprivileged home directory installations, you probably do not want
to specify --with-perl-lib=(site|vendor). Instead, you can use the
default perl installation target (below the irssi prefix). If you are
using local::lib you can also choose to install there by specifying
--with-perl-lib=$PERL_LOCAL_LIB_ROOT/lib/perl5
You can verify that the perl module is loaded and working with "/LOAD"
command. It should print something like:

24
NEWS
View File

@ -1,3 +1,27 @@
v0.8.21-head 2016-xx-xx The Irssi team <staff@irssi.org>
* Removed --disable-ipv6
* /connect Network now aborts with an error if no servers have been
added to that network.
* /dcc commands now use quotes around spaces consistently.
+ irssiproxy can now forward all tags through a single port.
+ irssiproxy can also listen on unix sockets.
+ send channel -botcmds immediately when no mask is specified (#175).
+ the kill buffer now remembers consecutive kills.
New bindings were added: yank_next_cutbuffer and append_next_kill
+ connections will avoid looking up IPv6 addresses if the machine does
not have an IPv6 address assigned (exact behaviour is implementation
defined).
+ Fix potential crash if scripts insert undef values into the completion
list.
+ Paste warning is now also shown on pasting overlong lines.
+ autolog_ignore_targets and activity_hide_targets learn a new syntax
tag/* and * to ignore whole networks or everything.
+ /hilight got a -matchcase flag to hilight case sensitively (#421).
- IP addresses are no longer stored when resolve_reverse_lookup is
used.
- /names and $[...] now uses utf8 string operations (#40, #411).
- Removed broken support for curses.
v0.8.20 2016-09-16 The Irssi team <staff@irssi.org>
- Correct the name of an emitted sasl signal (#484)
- Correct the prototype for the 'message private' signal (#515)

View File

@ -1,31 +0,0 @@
/* misc.. */
#undef HAVE_IPV6
#undef HAVE_SOCKS_H
#undef HAVE_STATIC_PERL
#undef HAVE_GMODULE
/* macros/curses checks */
#undef HAS_CURSES
#undef USE_SUNOS_CURSES
#undef USE_BSD_CURSES
#undef USE_SYSV_CURSES
#undef USE_NCURSES
#undef NO_COLOR_CURSES
#undef SCO_FLAVOR
/* our own curses checks */
#undef HAVE_NCURSES_USE_DEFAULT_COLORS
#undef HAVE_CURSES_IDCOK
#undef HAVE_CURSES_RESIZETERM
#undef HAVE_CURSES_WRESIZE
/* terminfo/termcap */
#undef HAVE_TERMINFO
/* 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

View File

@ -1,4 +1,4 @@
AC_INIT(irssi, 0.8.20)
AC_INIT(irssi, 0.8.21-head)
AC_CONFIG_SRCDIR([src])
AC_CONFIG_AUX_DIR(build-aux)
AC_PREREQ(2.50)
@ -61,15 +61,6 @@ AC_ARG_WITH(proxy,
fi,
want_irssiproxy=no)
AC_ARG_WITH(terminfo,
[ --without-terminfo Use curses backend instead of terminfo],
if test x$withval = xno; then
want_terminfo=no
else
want_terminfo=yes
fi,
want_terminfo=yes)
AC_ARG_WITH(modules,
[ --with-modules Specify what modules to build in binary],
if test x$withval != xyes -a x$withval != xno; then
@ -79,7 +70,6 @@ AC_ARG_WITH(modules,
if test "x$prefix" != "xNONE"; then
prefix=`eval echo $prefix`
PERL_MM_PARAMS="INSTALLDIRS=perl INSTALL_BASE=$prefix"
perl_library_dir="PERL_USE_LIB"
perl_set_use_lib=yes
perl_prefix_note=yes
@ -97,30 +87,35 @@ AC_ARG_WITH(perl-staticlib,
AC_ARG_WITH(perl-lib,
[ --with-perl-lib=[site|vendor|DIR] Specify where to install the
[ --with-perl-lib=[perl|site|vendor|DIR] Specify where to install the
Perl libraries for irssi, default is site],
if test "x$withval" = xyes; then
want_perl=yes
elif test "x$withval" = xno; then
want_perl=no
elif test "x$withval" = xperl; then
want_perl=yes
perl_prefix_note=no
PERL_MM_PARAMS="INSTALLDIRS=perl"
perl_set_use_lib=no
elif test "x$withval" = xsite; then
want_perl=yes
perl_prefix_note=no
PERL_MM_PARAMS=""
perl_set_use_lib=no
elif test "x$withval" = xvendor; then
want_perl=yes
perl_prefix_note=no
if test -z "`$perlpath -v|grep '5\.0'`"; then
PERL_MM_PARAMS="INSTALLDIRS=vendor"
else
PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`perl -e 'use Config; print $Config{prefix}'`"
PERL_MM_PARAMS="INSTALLDIRS=perl PREFIX=`$perlpath -e 'use Config; print $Config{prefix}'`"
fi
perl_library_dir="(vendor default - `$perlpath -e 'use Config; print $Config{archlib}'`)"
perl_set_use_lib=no
else
want_perl=yes
perl_prefix_note=no
PERL_MM_PARAMS="INSTALLDIRS=perl LIB=$withval"
perl_library_dir="PERL_USE_LIB"
perl_set_use_lib=yes
fi,
want_perl=yes)
@ -140,15 +135,6 @@ AC_ARG_WITH(perl,
fi,
want_perl=static)
AC_ARG_ENABLE(ipv6,
[ --disable-ipv6 Disable IPv6 support],
if test x$enableval = xno; then
want_ipv6=no
else
want_ipv6=yes
fi,
want_ipv6=yes)
AC_ARG_ENABLE(dane,
[ --enable-dane Enable DANE support],
if test x$enableval = xno ; then
@ -319,45 +305,19 @@ dnl **
dnl ** curses checks
dnl **
if test "x$want_textui" = "xyes"; then
AC_CHECK_CURSES
if test "x$want_textui" != "xno"; then
TEXTUI_LIBS="$CURSES_LIBS"
if test "x$has_curses" = "xtrue"; then
old_libs=$LIBS
LIBS="$LIBS $CURSES_LIBS"
if test $want_terminfo = no; then
AC_CHECK_FUNC(use_default_colors, AC_DEFINE(HAVE_NCURSES_USE_DEFAULT_COLORS))
AC_CHECK_FUNC(idcok, AC_DEFINE(HAVE_CURSES_IDCOK))
AC_CHECK_FUNC(resizeterm, AC_DEFINE(HAVE_CURSES_RESIZETERM))
AC_CHECK_FUNC(wresize, AC_DEFINE(HAVE_CURSES_WRESIZE))
fi
AC_CHECK_FUNC(setupterm,, [
want_termcap=yes
])
LIBS=$old_libs
else
AC_CHECK_LIB(tinfo, setupterm, [
TEXTUI_LIBS="-ltinfo"
want_terminfo=yes
], AC_CHECK_LIB(termlib, tgetent, [
TEXTUI_LIBS="-ltermlib"
want_termcap=yes
], AC_CHECK_LIB(termcap, tgetent, [
TEXTUI_LIBS="-ltermcap"
want_termcap=yes
], [
AC_ERROR(Terminfo/termcap not found - install libncurses-dev or ncurses-devel package)
want_textui=no
])))
fi
TEXTUI_NO_LIBS="$LIBS"
LIBS=
AC_SEARCH_LIBS([setupterm], [tinfo ncursesw ncurses], [want_textui=yes], [
AC_ERROR(Terminfo not found - install libncurses-dev or ncurses-devel package)
want_textui="no, Terminfo not found"
])
TEXTUI_LIBS="$LIBS"
AC_SUBST(TEXTUI_LIBS)
LIBS="$TEXTUI_NO_LIBS"
if test "x$want_termcap" = "xyes"; then
AC_CHECK_FUNC(tparm,, need_tparm=yes)
else
AC_DEFINE(HAVE_TERMINFO)
fi
fi
dnl **
@ -380,7 +340,7 @@ if test "$want_perl" != "no"; then
dnl * complain about them. Normally there's only few options
dnl * that we want to keep:
dnl * -Ddefine -Uundef -I/path -fopt -mopt
PERL_CFLAGS=`echo $PERL_CFLAGS | $perlpath -pe 's/^(.* )?-[^DUIfm][^ ]+/\1/g; s/^(.* )?\+[^ ]+/\1/g'`
PERL_CFLAGS=`echo $PERL_CFLAGS | $perlpath -pe 's/^(.* )?-@<:@^DUIfm@:>@@<:@^ @:>@+/\1/g; s/^(.* )?\+@<:@^ @:>@+/\1/g'`
PERL_EXTRA_OPTS="CCCDLFLAGS=\"-fPIC\""
AC_SUBST(PERL_EXTRA_OPTS)
@ -464,13 +424,37 @@ if test "$want_perl" != "no"; then
PERL_STATIC_LIBS=0
fi
# remove any prefix from PERL_MM_OPT
PERL_MM_OPT=`perl -MText::ParseWords -e 'sub qu{$_=shift;s{^(.*?)=(.*)$}{($a,$b)=($1,$2);$b=~s/"/\\\\"/g;qq{$a="$b"}}ge if /@<:@\s"@:>@/;$_} local $,=" "; print map qu($_), grep !/^(INSTALL_BASE|PREFIX)=/, shellwords(@ARGV)' "$PERL_MM_OPT"`
# figure out the correct @INC path - we'll need to do this
# through MakeMaker since it's difficult to get it right
# otherwise.
$perlpath -MExtUtils::MakeMaker -e 'WriteMakefile(NAME => "test", MAKEFILE => "Makefile.test", FIRST_MAKEFILE => "/dev/null", NO_META => 1, NO_MYMETA => 1);' $PERL_MM_PARAMS >/dev/null
echo 'show-INSTALLDIRS:' >> Makefile.test
echo ' @echo $(INSTALLDIRS)' >> Makefile.test
perl_INSTALLDIRS=`$am_make -s -f Makefile.test show-INSTALLDIRS`
if test "x$perl_INSTALLDIRS" = "xsite"; then
perl_library_dir="site default"
perl_INSTALL_VAR=INSTALLSITEARCH
elif test "x$perl_INSTALLDIRS" = "xvendor"; then
perl_library_dir="vendor default"
perl_INSTALL_VAR=INSTALLVENDORARCH
else
perl_library_dir="module default"
perl_INSTALL_VAR=INSTALLARCHLIB
fi
echo 'show-ARCHLIB:' >> Makefile.test
echo ' @echo $('"$perl_INSTALL_VAR"')' >> Makefile.test
perl_use_lib=`$am_make -s -f Makefile.test show-ARCHLIB`
rm -f Makefile.test
if test "x$perl_set_use_lib" = "xyes"; then
perl -e 'use ExtUtils::MakeMaker; WriteMakefile("NAME" => "test", "MAKEFILE" => "Makefile.test");' $PERL_MM_PARAMS >/dev/null
PERL_USE_LIB=`perl -e 'open(F, "Makefile.test"); while (<F>) { chomp; if (/^(\w+) = (.*$)/) { $keys{$1} = $2; } }; $key = $keys{INSTALLARCHLIB}; while ($key =~ /\\$\((\w+)\)/) { $value = $keys{$1}; $key =~ s/\\$\($1\)/$value/; }; print $key;'`
rm -f Makefile.test
if $perlpath -e 'exit ! grep $_ eq $ARGV@<:@0@:>@, grep /^\//, @INC' "$perl_use_lib"; then
perl_library_dir="other path in @INC"
perl_set_use_lib=no
else
perl_library_dir="prepends to @INC with /set perl_use_lib"
PERL_USE_LIB="$perl_use_lib"
fi
fi
AC_SUBST(perl_module_lib)
@ -486,6 +470,7 @@ if test "$want_perl" != "no"; then
AC_SUBST(PERL_CFLAGS)
AC_SUBST(PERL_USE_LIB)
AC_SUBST(PERL_MM_OPT)
AC_SUBST(PERL_MM_PARAMS)
AC_SUBST(PERL_STATIC_LIBS)
fi
@ -496,8 +481,6 @@ AM_CONDITIONAL(BUILD_TEXTUI, test "$want_textui" = "yes")
AM_CONDITIONAL(BUILD_IRSSIBOT, test "$want_irssibot" = "yes")
AM_CONDITIONAL(BUILD_IRSSIPROXY, test "$want_irssiproxy" = "yes")
AM_CONDITIONAL(HAVE_PERL, test "$want_perl" != "no")
AM_CONDITIONAL(NEED_TPARM, test "$need_tparm" = "yes")
AM_CONDITIONAL(USE_CURSES, test "$want_terminfo" != "yes" -a "$want_termcap" != "yes")
# move LIBS to PROG_LIBS so they're not tried to be used when linking eg. perl libraries
PROG_LIBS=$LIBS
@ -577,29 +560,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS"
AC_SUBST(COMMON_NOUI_LIBS)
AC_SUBST(COMMON_LIBS)
dnl **
dnl ** IPv6 support
dnl **
have_ipv6=no
if test "x$want_ipv6" = "xyes"; then
AC_MSG_CHECKING([for IPv6])
AC_CACHE_VAL(irssi_cv_type_in6_addr,
[AC_TRY_COMPILE([
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>],
[struct in6_addr i = in6addr_any; return &i == &i;],
have_ipv6=yes,
)])
if test $have_ipv6 = yes; then
AC_DEFINE(HAVE_IPV6)
fi
AC_MSG_RESULT($have_ipv6)
fi
have_dane=no
if test "x$want_dane" = "xyes"; then
AC_MSG_CHECKING([for DANE])
@ -617,12 +577,20 @@ if test "x$want_dane" = "xyes"; then
fi
fi
if test "x$want_truecolor" = "xyes" -a "x$want_termcap" != "xyes" -a "x$want_terminfo" = "xyes" ; then
if test "x$want_truecolor" = "xyes"; then
AC_DEFINE([TERM_TRUECOLOR], [], [true color support in terminal])
else
want_truecolor=no
fi
AH_TEMPLATE(HAVE_GMODULE)
AH_TEMPLATE(HAVE_SOCKS_H, [misc..])
AH_TEMPLATE(HAVE_STATIC_PERL)
AH_TEMPLATE(PRIuUOFF_T, [printf()-format for uoff_t, eg. "u" or "lu" or "llu"])
AH_TEMPLATE(UOFF_T_INT, [What type should be used for uoff_t])
AH_TEMPLATE(UOFF_T_LONG)
AH_TEMPLATE(UOFF_T_LONG_LONG)
AC_CONFIG_FILES([
Makefile
src/Makefile
@ -675,16 +643,7 @@ fi
echo
if test "x$want_textui" = "xno"; then
text=no
elif test "x$want_termcap" = "xyes"; then
text="yes, using termcap"
elif test "x$want_terminfo" = "xyes"; then
text="yes, using terminfo"
else
text="yes, using curses"
fi
echo "Building text frontend ........... : $text"
echo "Building text frontend ........... : $want_textui"
echo "Building irssi bot ............... : $want_irssibot"
echo "Building irssi proxy ............. : $want_irssiproxy"
if test "x$have_gmodule" = "xyes"; then
@ -724,13 +683,7 @@ if test "x$want_perl" != "xno" -a "x$perl_mod_error" != "x"; then
fi
if test "x$want_perl" != "xno"; then
if test "$perl_library_dir" = "PERL_USE_LIB"; then
perl_library_dir=$PERL_USE_LIB
fi
if test -z "$perl_library_dir"; then
perl_library_dir="(site default - `$perlpath -e 'use Config; print $Config{sitearch}'`)"
fi
echo "Perl library directory ........... : $perl_library_dir"
echo "Perl library directory ........... : ($perl_library_dir - $perl_use_lib)"
if test "x$perl_prefix_note" = "xyes"; then
echo " - NOTE: This was automatically set to the same directory you gave with"
echo " --prefix. If you want the perl libraries to install to their 'correct'"
@ -742,7 +695,6 @@ echo "Install prefix ................... : $prefix"
echo
echo "Building with IPv6 support ....... : $have_ipv6"
echo "Building with SSL support ........ : $have_openssl"
if test "x$have_openssl" = "xno" -a "x$enable_ssl" = "xyes"; then
if test -f /etc/debian_version; then

View File

@ -7,6 +7,7 @@
LIST: Displays the list of configured channels.
ADD: Adds a channel to your configuration.
MODIFY: Modifies a channel in your configuration.
REMOVE: Removes a channel from your configuration.
-auto: Automatically join the channel.
@ -36,6 +37,7 @@
/CHANNEL ADD -auto #basementcat Quakenet secret_lair
/CHANNEL ADD -auto -bots '*!@*.irssi.org *!bot@irssi.org' -botcmd 'msg $0 op WzerTrzq' #hideout Freenode
/CHANNEL ADD -auto -bots 'Q!TheQBot@CServe.quakenet.org' -botcmd '^MSG Q op #irssi' #irssi Quakenet
/CHANNEL MODIFY -noauto #irssi Freenode
/CHANNEL REMOVE #hideout Freenode
%9Special Example:%9

View File

@ -36,10 +36,10 @@
%9Examples:%9
/DCC CHAT mike
/DCC GET bob summer vacation.mkv
/DCC GET bob "summer vacation.mkv"
/DCC SEND sarah "summer vacation.mkv"
/DCC CLOSE get mike
/DCC CLOSE send bob summer vacation.mkv
/DCC CLOSE send bob "summer vacation.mkv"
%9See also:%9 CD

View File

@ -10,6 +10,7 @@
-line: Highlights the whole line.
-mask: Highlights all messages from users matching the mask.
-full: The text must match the full word.
-matchcase: The text must match case.
-regexp: The text is a regular expression.
-color: The color the display the highlight in.
-actcolor: The color to mark the highlight activity in the statusbar.

View File

@ -7,6 +7,7 @@
LIST: Displays the list of configured networks.
ADD: Adds a network to your configuration.
MODIFY: Modifies a network in your configuration.
REMOVE: Removes a network from your configuration.
-nick: Specifies the nickname to use.
@ -59,6 +60,7 @@
/NETWORK ADD -usermode +iw -nick mike -realname 'The one and only mike!' -host staff.irssi.org Freenode
/NETWORK ADD -autosendcmd '^MSG NickServ identify WzerT8zq' Freenode
/NETWORK ADD -autosendcmd '^MSG Q@CServe.quakenet.org AUTH mike WzerT8zq; WAIT 2000; OPER mike WzerT8zq; WAIT 2000; MODE mike +kXP' Quakenet
/NETWORK MODIFY -usermode +gi EFnet
/NETWORK REMOVE Freenode
%9See also:%9 CHANNEL, CONNECT, SERVER

View File

@ -12,6 +12,8 @@
RESET: Unloads all the scripts.
-permanent: In combination with EXEC, the code will be loaded into the
memory.
-autorun: When passed to RESET the scripts in the autorun folder are
reloaded.
If no argument is given, the list of active scripts will be displayed.

View File

@ -8,6 +8,7 @@
LIST: Displays the list of servers you are connected to.
CONNECT: Connects to the given server.
ADD: Adds a server to your configuration.
MODIFY: Modifies a server in your configuration.
REMOVE: Removes a server from your configuration.
PURGE: Purges the commands queued to be sent to the server.
@ -62,6 +63,7 @@
/SERVER CONNECT +chat.freenode.net
/SERVER ADD -network Freenode -noautosendcmd orwell.freenode.net
/SERVER ADD -! -auto -host staff.irssi.org -port 6667 -4 -network Freenode -noproxy orwell.freenode.net
/SERVER MODIFY -network Freenode -noauto orwell.freenode.net
/SERVER REMOVE orwell.freenode.net 6667 Freenode
/SERVER PURGE
/SERVER PURGE orwell.freenode.net

View File

@ -51,9 +51,6 @@ use
.I HOSTNAME
for your irc session
.TP
.BI "\-d, \-\-dummy"
use dummy terminal mode
.TP
.BI "\-v, \-\-version"
display the version of Irssi
.TP

View File

@ -30,6 +30,18 @@ There we have 3 different irc networks answering in 3 ports. Note that
you'll have to make the correct /IRCNET ADD and /SERVER ADD commands to
make it work properly.
The special network name "?" allows the client to select the network
dynamically on connect:
/SET irssiproxy_ports ?=2777
Now the client can send <network>:<password> as the server password, e.g.
/CONNECT ... 2777 efnet:secret
to connect to efnet. If there is no irssiproxy_password set, you can
omit the ":" and just send the network name as the password.
By default, the proxy binds to all available interfaces. To make it
only listen on (for example) the loopback address:

View File

@ -497,6 +497,13 @@
<pre><code> /SET irssiproxy_ports *=2777
</code></pre>
<p>The special network name <code>?</code> allows the client to select the
network dynamically on connect (see below):</p>
<pre>
/SET irssiproxy_ports ?=2777
</pre>
<p>Usage in client side:</p>
<p>Just connect to the irssi proxy like it is a normal server with password specified in <code>/SET irssiproxy_password</code>. For example:</p>
@ -505,6 +512,16 @@
/SERVER ADD -network efnet my.irssi-proxy.org 2778 secret
</code></pre>
<p>Or, if you used <code>?</code> in <code>irssiproxy_ports</code>:</p>
<pre>
/SERVER ADD -network IRCnet my.irssi-proxy.org 2777 IRCnet:secret
/SERVER ADD -network efnet my.irssi-proxy.org 2777 efnet:secret
</pre>
<p>I.e. the network to connect to is specified as part of the password,
separated by <code>:</code> from the actual proxy password.</p>
<p>Irssi proxy works fine with other IRC clients as well.</p>
<p><strong>SOCKS</strong></p>

View File

@ -1,298 +0,0 @@
dnl Curses detection: Munged from Midnight Commander's configure.in
dnl
dnl What it does:
dnl =============
dnl
dnl - Determine which version of curses is installed on your system
dnl and set the -I/-L/-l compiler entries and add a few preprocessor
dnl symbols
dnl - Do an AC_SUBST on the CURSES_INCLUDEDIR and CURSES_LIBS so that
dnl @CURSES_INCLUDEDIR@ and @CURSES_LIBS@ will be available in
dnl Makefile.in's
dnl - Modify the following configure variables (these are the only
dnl curses.m4 variables you can access from within configure.in)
dnl CURSES_INCLUDEDIR - contains -I's and possibly -DRENAMED_CURSES if
dnl an ncurses.h that's been renamed to curses.h
dnl is found.
dnl CURSES_LIBS - sets -L and -l's appropriately
dnl CFLAGS - if --with-sco, add -D_SVID3
dnl has_curses - exports result of tests to rest of configure
dnl
dnl Usage:
dnl ======
dnl 1) Add lines indicated below to acconfig.h
dnl 2) call AC_CHECK_CURSES after AC_PROG_CC in your configure.in
dnl 3) Instead of #include <curses.h> you should use the following to
dnl properly locate ncurses or curses header file
dnl
dnl #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
dnl #include <ncurses.h>
dnl #else
dnl #include <curses.h>
dnl #endif
dnl
dnl 4) Make sure to add @CURSES_INCLUDEDIR@ to your preprocessor flags
dnl 5) Make sure to add @CURSES_LIBS@ to your linker flags or LIBS
dnl
dnl Notes with automake:
dnl - call AM_CONDITIONAL(HAS_CURSES, test "$has_curses" = true) from
dnl configure.in
dnl - your Makefile.am can look something like this
dnl -----------------------------------------------
dnl INCLUDES= blah blah blah $(CURSES_INCLUDEDIR)
dnl if HAS_CURSES
dnl CURSES_TARGETS=name_of_curses_prog
dnl endif
dnl bin_PROGRAMS = other_programs $(CURSES_TARGETS)
dnl other_programs_SOURCES = blah blah blah
dnl name_of_curses_prog_SOURCES = blah blah blah
dnl other_programs_LDADD = blah
dnl name_of_curses_prog_LDADD = blah $(CURSES_LIBS)
dnl -----------------------------------------------
dnl
dnl
dnl The following lines should be added to acconfig.h:
dnl ==================================================
dnl
dnl /*=== Curses version detection defines ===*/
dnl /* Found some version of curses that we're going to use */
dnl #undef HAS_CURSES
dnl
dnl /* Use SunOS SysV curses? */
dnl #undef USE_SUNOS_CURSES
dnl
dnl /* Use old BSD curses - not used right now */
dnl #undef USE_BSD_CURSES
dnl
dnl /* Use SystemV curses? */
dnl #undef USE_SYSV_CURSES
dnl
dnl /* Use Ncurses? */
dnl #undef USE_NCURSES
dnl
dnl /* If you Curses does not have color define this one */
dnl #undef NO_COLOR_CURSES
dnl
dnl /* Define if you want to turn on SCO-specific code */
dnl #undef SCO_FLAVOR
dnl
dnl /* Set to reflect version of ncurses *
dnl * 0 = version 1.*
dnl * 1 = version 1.9.9g
dnl * 2 = version 4.0/4.1 */
dnl #undef NCURSES_970530
dnl
dnl /*=== End new stuff for acconfig.h ===*/
dnl
AC_DEFUN([AC_CHECK_CURSES],[
search_ncurses=true
screen_manager=""
has_curses=false
CFLAGS=${CFLAGS--O}
AC_SUBST(CURSES_LIBS)
AC_SUBST(CURSES_INCLUDEDIR)
AC_ARG_WITH(sco,
[ --with-sco Use this to turn on SCO-specific code],[
if test x$withval = xyes; then
AC_DEFINE(SCO_FLAVOR)
CFLAGS="$CFLAGS -D_SVID3"
fi
])
AC_ARG_WITH(sunos-curses,
[ --with-sunos-curses Used to force SunOS 4.x curses],[
if test x$withval = xyes; then
AC_USE_SUNOS_CURSES
fi
])
AC_ARG_WITH(osf1-curses,
[ --with-osf1-curses Used to force OSF/1 curses],[
if test x$withval = xyes; then
AC_USE_OSF1_CURSES
fi
])
AC_ARG_WITH(vcurses,
[[ --with-vcurses[=incdir] Used to force SysV curses]],
if test x$withval != xyes; then
CURSES_INCLUDEDIR="-I$withval"
fi
AC_USE_SYSV_CURSES
)
AC_ARG_WITH(ncurses,
[[ --with-ncurses[=dir] Compile with ncurses/locate base dir]],
if test x$withval = xno ; then
search_ncurses=false
elif test x$withval != xyes ; then
AC_NCURSES($withval/include, ncurses.h, -L$withval/lib -lncurses, -I$withval/include, [ncurses on $withval/include])
fi
)
if $search_ncurses
then
AC_SEARCH_NCURSES()
fi
])
AC_DEFUN([AC_USE_SUNOS_CURSES], [
search_ncurses=false
screen_manager="SunOS 4.x /usr/5include curses"
AC_MSG_RESULT(Using SunOS 4.x /usr/5include curses)
AC_DEFINE(USE_SUNOS_CURSES)
AC_DEFINE(HAS_CURSES)
has_curses=true
AC_DEFINE(NO_COLOR_CURSES)
AC_DEFINE(USE_SYSV_CURSES)
CURSES_INCLUDEDIR="-I/usr/5include"
CURSES_LIBS="/usr/5lib/libcurses.a /usr/5lib/libtermcap.a"
AC_MSG_RESULT(Please note that some screen refreshs may fail)
])
AC_DEFUN([AC_USE_OSF1_CURSES], [
AC_MSG_RESULT(Using OSF1 curses)
search_ncurses=false
screen_manager="OSF1 curses"
AC_DEFINE(HAS_CURSES)
has_curses=true
AC_DEFINE(NO_COLOR_CURSES)
AC_DEFINE(USE_SYSV_CURSES)
CURSES_LIBS="-lcurses"
])
AC_DEFUN([AC_USE_SYSV_CURSES], [
AC_MSG_RESULT(Using SysV curses)
AC_DEFINE(HAS_CURSES)
has_curses=true
AC_DEFINE(USE_SYSV_CURSES)
search_ncurses=false
screen_manager="SysV/curses"
CURSES_LIBS="-lcurses"
])
dnl AC_ARG_WITH(bsd-curses,
dnl [--with-bsd-curses Used to compile with bsd curses, not very fancy],
dnl search_ncurses=false
dnl screen_manager="Ultrix/cursesX"
dnl if test $system = ULTRIX
dnl then
dnl THIS_CURSES=cursesX
dnl else
dnl THIS_CURSES=curses
dnl fi
dnl
dnl CURSES_LIBS="-l$THIS_CURSES -ltermcap"
dnl AC_DEFINE(HAS_CURSES)
dnl has_curses=true
dnl AC_DEFINE(USE_BSD_CURSES)
dnl AC_MSG_RESULT(Please note that some screen refreshs may fail)
dnl AC_WARN(Use of the bsdcurses extension has some)
dnl AC_WARN(display/input problems.)
dnl AC_WARN(Reconsider using xcurses)
dnl)
dnl
dnl Parameters: directory filename curses_LIBS curses_INCLUDEDIR nicename
dnl
AC_DEFUN([AC_NCURSES], [
if $search_ncurses
then
if test -f $1/$2
then
AC_MSG_RESULT(Found ncurses on $1/$2)
CURSES_LIBS="$3"
AC_CHECK_LIB(ncurses, initscr, [
true;
], [
CHECKLIBS=`echo "$3"|sed 's/-lncurses/-lcurses/g'`
AC_CHECK_LIB(curses, initscr, [
CURSES_LIBS="$CHECKLIBS"
],, $CHECKLIBS)
], $CURSES_LIBS)
CURSES_INCLUDEDIR="$4"
search_ncurses=false
screen_manager="$5"
AC_DEFINE(HAS_CURSES)
has_curses=true
has_ncurses=true
AC_DEFINE(USE_NCURSES)
fi
fi
])
AC_DEFUN([AC_SEARCH_NCURSES], [
AC_CHECKING("location of ncurses.h file")
AC_NCURSES(/usr/include, ncurses.h, -lncurses,,
[ncurses in /usr/include])
AC_NCURSES(/usr/include/ncurses, ncurses.h, -lncurses, -I/usr/include/ncurses,
[ncurses in /usr/include/ncurses])
AC_NCURSES(/usr/local/include, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include,
[ncurses in /usr/local/include])
AC_NCURSES(/usr/local/include/ncurses, ncurses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses,
[ncurses in /usr/local/include/ncurses])
AC_NCURSES(/usr/local/include/ncurses, curses.h, -L/usr/local/lib -lncurses, -I/usr/local/include/ncurses -DRENAMED_NCURSES,
[renamed ncurses in /usr/local/include/ncurses])
AC_NCURSES(/usr/include/ncurses, curses.h, -lncurses, -I/usr/include/ncurses -DRENAMED_NCURSES,
[renamed ncurses in /usr/include/ncurses])
dnl
dnl We couldn't find ncurses, try SysV curses
dnl
if $search_ncurses
then
AC_EGREP_HEADER(init_color, /usr/include/curses.h,
AC_USE_SYSV_CURSES)
AC_EGREP_CPP(USE_NCURSES,[
#include <curses.h>
#ifdef __NCURSES_H
#undef USE_NCURSES
USE_NCURSES
#endif
],[
CURSES_INCLUDEDIR="$CURSES_INCLUDEDIR -DRENAMED_NCURSES"
AC_DEFINE(HAS_CURSES)
has_curses=true
has_ncurses=true
AC_DEFINE(USE_NCURSES)
search_ncurses=false
screen_manager="ncurses installed as curses"
])
fi
dnl
dnl Try SunOS 4.x /usr/5{lib,include} ncurses
dnl The flags USE_SUNOS_CURSES, USE_BSD_CURSES and BUGGY_CURSES
dnl should be replaced by a more fine grained selection routine
dnl
if $search_ncurses
then
if test -f /usr/5include/curses.h
then
AC_USE_SUNOS_CURSES
fi
fi
dnl use whatever curses there happens to be
if $search_ncurses
then
if test -f /usr/include/curses.h
then
CURSES_LIBS="-lcurses"
AC_DEFINE(HAS_CURSES)
has_curses=true
search_ncurses=false
screen_manager="curses"
fi
fi
])

View File

@ -6,7 +6,7 @@
#define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */
#define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */
#define IRSSI_ABI_VERSION 2
#define IRSSI_ABI_VERSION 6
#define DEFAULT_SERVER_ADD_PORT 6667

View File

@ -44,6 +44,8 @@ libcore_a_SOURCES = \
settings.c \
signals.c \
special-vars.c \
utf8.c \
wcwidth.c \
write-buffer.c
structure_headers = \
@ -93,6 +95,7 @@ pkginc_core_HEADERS = \
settings.h \
signals.h \
special-vars.h \
utf8.h \
window-item-def.h \
write-buffer.h \
$(structure_headers)

View File

@ -231,6 +231,31 @@ static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag)
/* Send the auto send command to channel */
void channel_send_autocommands(CHANNEL_REC *channel)
{
CHANNEL_SETUP_REC *rec;
g_return_if_fail(IS_CHANNEL(channel));
if (channel->session_rejoin)
return;
rec = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
return;
/* if the autosendcmd alone (with no -bots parameter) has been
* specified then send it right after joining the channel, when
* the WHO list hasn't been yet retrieved.
* Depending on the value of the 'channel_max_who_sync' option
* the WHO list might not be retrieved after the join event. */
if (rec->botmasks == NULL || !*rec->botmasks) {
/* just send the command. */
eval_special_string(rec->autosendcmd, "", channel->server, channel);
}
}
void channel_send_botcommands(CHANNEL_REC *channel)
{
CHANNEL_SETUP_REC *rec;
NICK_REC *nick;
@ -245,11 +270,9 @@ void channel_send_autocommands(CHANNEL_REC *channel)
if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
return;
if (rec->botmasks == NULL || !*rec->botmasks) {
/* just send the command. */
eval_special_string(rec->autosendcmd, "", channel->server, channel);
/* this case has already been handled by channel_send_autocommands */
if (rec->botmasks == NULL || !*rec->botmasks)
return;
}
/* find first available bot.. */
bots = g_strsplit(rec->botmasks, " ", -1);

View File

@ -31,6 +31,7 @@ void channel_change_visible_name(CHANNEL_REC *channel, const char *name);
/* Send the auto send command to channel */
void channel_send_autocommands(CHANNEL_REC *channel);
void channel_send_botcommands(CHANNEL_REC *channel);
void channels_init(void);
void channels_deinit(void);

View File

@ -73,6 +73,13 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
conn = server_create_conn(proto != NULL ? proto->id : -1, addr,
atoi(portstr), chatnet, password, nick);
if (conn == NULL) {
signal_emit("error command", 1,
GINT_TO_POINTER(CMDERR_NO_SERVER_DEFINED));
cmd_params_free(free_arg);
return NULL;
}
if (proto == NULL)
proto = chat_protocol_find_id(conn->chat_type);
@ -117,8 +124,8 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
if (g_hash_table_lookup(optlist, "!") != NULL)
conn->no_autojoin_channels = TRUE;
if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noautosendcmd") != NULL)
conn->no_autosendcmd = TRUE;
if (g_hash_table_lookup(optlist, "noproxy") != NULL)
g_free_and_null(conn->proxy);

View File

@ -41,7 +41,8 @@ enum {
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 */
CMDERR_PROGRAM_NOT_FOUND, /* program not found */
CMDERR_NO_SERVER_DEFINED, /* no server has been defined for a given chatnet */
};
/* Return the full command for `alias' */

View File

@ -241,7 +241,7 @@ IGNORE_REC *ignore_find_full(const char *servertag, const char *mask, const char
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
if (strarray_length(channels) != strarray_length(rec->channels))
if (g_strv_length(channels) != g_strv_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */

View File

@ -114,7 +114,7 @@ int log_start_logging(LOG_REC *log)
/* path may contain variables (%time, $vars),
make sure the directory is created */
dir = g_path_get_dirname(log->real_fname);
mkpath(dir, log_dir_create_mode);
g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
}

View File

@ -20,6 +20,7 @@
#include "module.h"
#include "misc.h"
#include "commands.h"
#ifdef HAVE_REGEX_H
# include <regex.h>
@ -150,27 +151,13 @@ int find_substr(const char *list, const char *item)
return FALSE;
}
int strarray_length(char **array)
{
int len;
g_return_val_if_fail(array != NULL, 0);
len = 0;
while (*array) {
len++;
array++;
}
return len;
}
int strarray_find(char **array, const char *item)
{
char **tmp;
int index;
g_return_val_if_fail(array != NULL, 0);
g_return_val_if_fail(item != NULL, 0);
g_return_val_if_fail(array != NULL, -1);
g_return_val_if_fail(item != NULL, -1);
index = 0;
for (tmp = array; *tmp != NULL; tmp++, index++) {
@ -279,14 +266,23 @@ void hash_save_key(char *key, void *value, GSList **list)
*list = g_slist_append(*list, key);
}
/* save all keys in hash table to linked list - you shouldn't remove any
items while using this list, use g_slist_free() after you're done with it */
GSList *hashtable_get_keys(GHashTable *hash)
/* remove all the options from the optlist hash table that are valid for the
* command cmd */
GList *optlist_remove_known(const char *cmd, GHashTable *optlist)
{
GSList *list;
GList *list, *tmp, *next;
list = g_hash_table_get_keys(optlist);
if (cmd != NULL && list != NULL) {
for (tmp = list; tmp != NULL; tmp = next) {
char *option = tmp->data;
next = tmp->next;
if (command_have_option(cmd, option))
list = g_list_remove(list, option);
}
}
list = NULL;
g_hash_table_foreach(hash, (GHFunc) hash_save_key, &list);
return list;
}
@ -389,62 +385,6 @@ char *stristr_full(const char *data, const char *key)
return strstr_full_case(data, key, TRUE);
}
int regexp_match(const char *str, const char *regexp)
{
#ifdef HAVE_REGEX_H
regex_t preg;
int ret;
if (regcomp(&preg, regexp, REG_EXTENDED|REG_ICASE|REG_NOSUB) != 0)
return 0;
ret = regexec(&preg, str, 0, NULL, 0);
regfree(&preg);
return ret == 0;
#else
return FALSE;
#endif
}
/* Create the directory and all it's parent directories */
int mkpath(const char *path, int mode)
{
struct stat statbuf;
const char *p;
char *dir;
g_return_val_if_fail(path != NULL, -1);
p = g_path_skip_root((char *) path);
if (p == NULL) {
/* not a full path, maybe not what we wanted
but continue anyway.. */
p = path;
}
for (;;) {
if (*p != G_DIR_SEPARATOR && *p != '\0') {
p++;
continue;
}
dir = g_strndup(path, (int) (p-path));
if (stat(dir, &statbuf) != 0) {
if (mkdir(dir, mode) == -1)
{
g_free(dir);
return -1;
}
}
g_free(dir);
if (*p++ == '\0')
break;
}
return 0;
}
/* convert ~/ to $HOME */
char *convert_home(const char *path)
{
@ -471,22 +411,15 @@ int g_istr_cmp(gconstpointer v, gconstpointer v2)
return g_ascii_strcasecmp((const char *) v, (const char *) v2);
}
/* a char* hash function from ASU */
unsigned int g_istr_hash(gconstpointer v)
guint g_istr_hash(gconstpointer v)
{
const char *s = (const char *) v;
unsigned int h = 0, g;
const signed char *p;
guint32 h = 5381;
while (*s != '\0') {
h = (h << 4) + i_toupper(*s);
if ((g = h & 0xf0000000UL)) {
h = h ^ (g >> 24);
h = h ^ g;
}
s++;
}
for (p = v; *p != '\0'; p++)
h = (h << 5) + h + g_ascii_toupper(*p);
return h /* % M */;
return h;
}
/* Find `mask' from `data', you can use * and ? wildcards. */
@ -592,15 +525,11 @@ int dec2octal(int decimal)
/* string -> uoff_t */
uoff_t str_to_uofft(const char *str)
{
uoff_t ret;
ret = 0;
while (*str != '\0') {
ret = ret*10 + (*str - '0');
str++;
}
return ret;
#ifdef UOFF_T_LONG_LONG
return (uoff_t)strtoull(str, NULL, 10);
#else
return (uoff_t)strtoul(str, NULL, 10);
#endif
}
/* convert all low-ascii (<32) to ^<A..> combinations */
@ -811,20 +740,6 @@ char *escape_string(const char *str)
return ret;
}
int strocpy(char *dest, const char *src, size_t dstsize)
{
if (dstsize == 0)
return -1;
while (*src != '\0' && dstsize > 1) {
*dest++ = *src++;
dstsize--;
}
*dest++ = '\0';
return *src == '\0' ? 0 : -1;
}
int nearest_power(int num)
{
int n = 1;

View File

@ -32,15 +32,8 @@ char *gslistptr_to_string(GSList *list, int offset, const char *delimiter);
/* `list' contains char* */
char *gslist_to_string(GSList *list, const char *delimiter);
/* save all keys in hash table to linked list - you shouldn't remove any
items while using this list, use g_slist_free() after you're done with it */
GSList *hashtable_get_keys(GHashTable *hash);
GList *optlist_remove_known(const char *cmd, GHashTable *optlist);
/* easy way to check if regexp matches */
int regexp_match(const char *str, const char *regexp);
/* Create the directory and all it's parent directories */
int mkpath(const char *path, int mode);
/* convert ~/ to $HOME */
char *convert_home(const char *path);
@ -85,9 +78,6 @@ int parse_size(const char *size, int *bytes);
Stop when `end_char' is found from string. */
int is_numeric(const char *str, char end_char);
/* Like strlcpy(), but return -1 if buffer was overflown, 0 if not. */
int strocpy(char *dest, const char *src, size_t dstsize);
/* strstr() with case-ignoring */
char *stristr(const char *data, const char *key);
@ -107,8 +97,6 @@ char *show_lowascii(const char *str);
/* replace all `from' chars in string to `to' chars. returns `str' */
char *replace_chars(char *str, char from, char to);
/* return how many items `array' has */
int strarray_length(char **array);
/* return index of `item' in `array' or -1 if not found */
int strarray_find(char **array, const char *item);

View File

@ -30,17 +30,11 @@
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif
};
#ifdef HAVE_IPV6
# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
#define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
sizeof(so.sin6) : sizeof(so.sin))
#else
# define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
#endif
GIOChannel *g_io_channel_new(int handle)
{
@ -56,7 +50,7 @@ GIOChannel *g_io_channel_new(int handle)
IPADDR ip4_any = {
AF_INET,
#if defined(HAVE_IPV6) && defined(IN6ADDR_ANY_INIT)
#if defined(IN6ADDR_ANY_INIT)
IN6ADDR_ANY_INIT
#else
{ INADDR_ANY }
@ -68,10 +62,8 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
if (ip1->family != ip2->family)
return 0;
#ifdef HAVE_IPV6
if (ip1->family == AF_INET6)
return memcmp(&ip1->ip, &ip2->ip, sizeof(ip1->ip)) == 0;
#endif
return memcmp(&ip1->ip, &ip2->ip, 4) == 0;
}
@ -80,22 +72,16 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2)
static void sin_set_ip(union sockaddr_union *so, const IPADDR *ip)
{
if (ip == NULL) {
#ifdef HAVE_IPV6
so->sin6.sin6_family = AF_INET6;
so->sin6.sin6_addr = in6addr_any;
#else
so->sin.sin_family = AF_INET;
so->sin.sin_addr.s_addr = INADDR_ANY;
#endif
return;
}
so->sin.sin_family = ip->family;
#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&so->sin6.sin6_addr, &ip->ip, sizeof(ip->ip));
else
#endif
memcpy(&so->sin.sin_addr, &ip->ip, 4);
}
@ -103,31 +89,25 @@ void sin_get_ip(const union sockaddr_union *so, IPADDR *ip)
{
ip->family = so->sin.sin_family;
#ifdef HAVE_IPV6
if (ip->family == AF_INET6)
memcpy(&ip->ip, &so->sin6.sin6_addr, sizeof(ip->ip));
else
#endif
memcpy(&ip->ip, &so->sin.sin_addr, 4);
}
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((unsigned short)port);
else
#endif
so->sin.sin_port = htons((unsigned short)port);
}
static int sin_get_port(union sockaddr_union *so)
{
#ifdef HAVE_IPV6
if (so->sin.sin_family == AF_INET6)
return ntohs(so->sin6.sin6_port);
#endif
return ntohs(so->sin.sin_port);
return ntohs((so->sin.sin_family == AF_INET6) ?
so->sin6.sin6_port :
so->sin.sin_port);
}
/* Connect to socket */
@ -272,7 +252,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
/* create the socket */
handle = socket(so.sin.sin_family, SOCK_STREAM, 0);
#ifdef HAVE_IPV6
if (handle == -1 && (errno == EINVAL || errno == EAFNOSUPPORT)) {
/* IPv6 is not supported by OS */
so.sin.sin_family = AF_INET;
@ -280,7 +260,7 @@ GIOChannel *net_listen(IPADDR *my_ip, int *port)
handle = socket(AF_INET, SOCK_STREAM, 0);
}
#endif
if (handle == -1)
return NULL;
@ -399,23 +379,18 @@ int net_getsockname(GIOChannel *handle, IPADDR *addr, int *port)
Returns 0 = ok, others = error code for net_gethosterror() */
int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
{
#ifdef HAVE_IPV6
union sockaddr_union *so;
struct addrinfo hints, *ai, *ailist;
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);
memset(ip4, 0, sizeof(IPADDR));
memset(ip6, 0, sizeof(IPADDR));
#ifdef HAVE_IPV6
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
/* save error to host_error for later use */
ret = getaddrinfo(addr, NULL, &hints, &ailist);
@ -454,85 +429,40 @@ int net_gethostbyname(const char *addr, IPADDR *ip4, IPADDR *ip6)
}
freeaddrinfo(ailist);
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_list[rand() % count], 4);
return 0;
#endif
}
/* Get name for host, *name should be g_free()'d unless it's NULL.
Return values are the same as with net_gethostbyname() */
int net_gethostbyaddr(IPADDR *ip, char **name)
{
#ifdef HAVE_IPV6
union sockaddr_union so;
int host_error;
char hostname[NI_MAXHOST];
#else
struct hostent *hp;
#endif
g_return_val_if_fail(ip != NULL, -1);
g_return_val_if_fail(name != NULL, -1);
*name = NULL;
#ifdef HAVE_IPV6
memset(&so, 0, sizeof(so));
sin_set_ip(&so, ip);
/* save error to host_error for later use */
host_error = getnameinfo((struct sockaddr *) &so, sizeof(so),
hostname, sizeof(hostname), NULL, 0, 0);
host_error = getnameinfo((struct sockaddr *)&so, sizeof(so),
hostname, sizeof(hostname),
NULL, 0,
NI_NAMEREQD);
if (host_error != 0)
return host_error;
*name = g_strdup(hostname);
#else
if (ip->family != AF_INET) return -1;
hp = gethostbyaddr((const char *) &ip->ip, 4, AF_INET);
if (hp == NULL) return -1;
*name = g_strdup(hp->h_name);
#endif
return 0;
}
int net_ip2host(IPADDR *ip, char *host)
{
#ifdef HAVE_IPV6
if (!inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN))
return -1;
#else
unsigned long ip4;
if (ip->family != AF_INET) {
strcpy(host, "0.0.0.0");
} else {
ip4 = ntohl(ip->ip.s_addr);
g_snprintf(host, MAX_IP_LEN, "%lu.%lu.%lu.%lu",
(ip4 & 0xff000000UL) >> 24,
(ip4 & 0x00ff0000) >> 16,
(ip4 & 0x0000ff00) >> 8,
(ip4 & 0x000000ff));
}
#endif
return 0;
return inet_ntop(ip->family, &ip->ip, host, MAX_IP_LEN) ? 0 : -1;
}
int net_host2ip(const char *host, IPADDR *ip)
@ -542,12 +472,8 @@ int net_host2ip(const char *host, IPADDR *ip)
if (strchr(host, ':') != NULL) {
/* IPv6 */
ip->family = AF_INET6;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, host, &ip->ip) == 0)
return -1;
#else
ip->ip.s_addr = 0;
#endif
} else {
/* IPv4 */
ip->family = AF_INET;
@ -582,7 +508,6 @@ int net_geterror(GIOChannel *handle)
/* get error of net_gethostname() */
const char *net_gethosterror(int error)
{
#ifdef HAVE_IPV6
g_return_val_if_fail(error != 0, NULL);
if (error == EAI_SYSTEM) {
@ -590,36 +515,17 @@ const char *net_gethosterror(int error)
} else {
return gai_strerror(error);
}
#else
switch (error) {
case HOST_NOT_FOUND:
return "Host not found";
case NO_ADDRESS:
return "No IP address found for name";
case NO_RECOVERY:
return "A non-recovable name server error occurred";
case TRY_AGAIN:
return "A temporary error on an authoritative name server";
}
/* unknown error */
return NULL;
#endif
}
/* return TRUE if host lookup failed because it didn't exist (ie. not
some error with name server) */
int net_hosterror_notfound(int error)
{
#ifdef HAVE_IPV6
#ifdef EAI_NODATA /* NODATA is deprecated */
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
}
/* Get name of TCP service */

View File

@ -21,19 +21,11 @@
struct _IPADDR {
unsigned short family;
#ifdef HAVE_IPV6
struct in6_addr ip;
#else
struct in_addr ip;
#endif
};
/* maxmimum string length of IP address */
#ifdef HAVE_IPV6
# define MAX_IP_LEN INET6_ADDRSTRLEN
#else
# define MAX_IP_LEN 20
#endif
#define MAX_IP_LEN INET6_ADDRSTRLEN
#define IPADDR_IS_V6(ip) ((ip)->family != AF_INET)
@ -45,7 +37,7 @@ GIOChannel *g_io_channel_new(int handle);
int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
/* Connect to socket */
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip) G_GNUC_DEPRECATED;
/* Connect to socket with ip address and SSL*/
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server);
int irssi_ssl_handshake(GIOChannel *handle);

View File

@ -150,13 +150,18 @@ void rawlog_save(RAWLOG_REC *rawlog, const char *fname)
int f;
dir = g_path_get_dirname(fname);
mkpath(dir, log_dir_create_mode);
g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
path = convert_home(fname);
f = open(path, O_WRONLY | O_APPEND | O_CREAT, log_file_create_mode);
g_free(path);
if (f < 0) {
g_warning("rawlog open() failed: %s", strerror(errno));
return;
}
rawlog_dump(rawlog, f);
close(f);
}

View File

@ -190,6 +190,7 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
dest->away_reason = g_strdup(src->away_reason);
dest->no_autojoin_channels = src->no_autojoin_channels;
dest->no_autosendcmd = src->no_autosendcmd;
dest->unix_socket = src->unix_socket;
dest->use_ssl = src->use_ssl;
dest->ssl_cert = g_strdup(src->ssl_cert);

View File

@ -321,8 +321,8 @@ server_create_conn(int chat_type, const char *dest, int port,
chatrec = chatnet_find(dest);
if (chatrec != NULL) {
rec = create_chatnet_conn(chatrec->name, port, password, nick);
if (rec != NULL)
return rec;
/* If rec is NULL the chatnet has no url to connect to */
return rec;
}
chatrec = chatnet == NULL ? NULL : chatnet_find(chatnet);

View File

@ -684,21 +684,11 @@ SERVER_REC *cmd_options_get_server(const char *cmd,
SERVER_REC *defserver)
{
SERVER_REC *server;
GSList *list, *tmp, *next;
GList *list;
/* get all the options, then remove the known ones. there should
be only one left - the server tag. */
list = hashtable_get_keys(optlist);
if (cmd != NULL) {
for (tmp = list; tmp != NULL; tmp = next) {
char *option = tmp->data;
next = tmp->next;
if (command_have_option(cmd, option))
list = g_slist_remove(list, option);
}
}
list = optlist_remove_known(cmd, optlist);
if (list == NULL)
return defserver;
@ -713,7 +703,7 @@ SERVER_REC *cmd_options_get_server(const char *cmd,
server = NULL;
}
g_slist_free(list);
g_list_free(list);
return server;
}

View File

@ -148,11 +148,36 @@ int settings_get_size(const char *key)
return str == NULL ? 0 : bytes;
}
int settings_get_choice(const char *key)
{
SETTINGS_REC *rec;
CONFIG_NODE *node;
char *str;
int index;
rec = settings_get(key, SETTING_TYPE_CHOICE);
if (rec == NULL) return -1;
node = iconfig_node_traverse("settings", FALSE);
node = node == NULL ? NULL : iconfig_node_section(node, rec->module, -1);
str = node == NULL ? rec->default_value.v_string :
config_node_get_str(node, key, rec->default_value.v_string);
if (str == NULL || (index = strarray_find(rec->choices, str)) < 0)
return rec->default_value.v_int;
return index;
}
char *settings_get_print(SETTINGS_REC *rec)
{
char *value = NULL;
switch(rec->type) {
case SETTING_TYPE_CHOICE:
value = g_strdup(rec->choices[settings_get_choice(rec->key)]);
break;
case SETTING_TYPE_BOOLEAN:
value = g_strdup(settings_get_bool(rec->key) ? "ON" : "OFF");
break;
@ -172,13 +197,31 @@ char *settings_get_print(SETTINGS_REC *rec)
static void settings_add(const char *module, const char *section,
const char *key, SettingType type,
const SettingValue *default_value)
const SettingValue *default_value,
const char *choices)
{
SETTINGS_REC *rec;
char **choices_vec = NULL;
g_return_if_fail(key != NULL);
g_return_if_fail(section != NULL);
if (type == SETTING_TYPE_CHOICE) {
if (choices == NULL) {
g_warning("Trying to add setting '%s' with no choices.", key);
return;
}
choices_vec = g_strsplit(choices, ";", -1);
/* validate the default value */
if (default_value->v_int < 0 || default_value->v_int >= g_strv_length(choices_vec)) {
g_warning("Trying to add setting '%s' with an invalid default value.", key);
g_strfreev(choices_vec);
return;
}
}
rec = g_hash_table_lookup(settings, key);
if (rec != NULL) {
/* Already exists, make sure it's correct type */
@ -197,6 +240,7 @@ static void settings_add(const char *module, const char *section,
rec->type = type;
rec->default_value = *default_value;
rec->choices = choices_vec;
g_hash_table_insert(settings, rec->key, rec);
}
}
@ -208,7 +252,17 @@ void settings_add_str_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
settings_add(module, section, key, SETTING_TYPE_STRING, &default_value);
settings_add(module, section, key, SETTING_TYPE_STRING, &default_value, NULL);
}
void settings_add_choice_module(const char *module, const char *section,
const char *key, int def, const char *choices)
{
SettingValue default_value;
memset(&default_value, 0, sizeof(default_value));
default_value.v_int = def;
settings_add(module, section, key, SETTING_TYPE_CHOICE, &default_value, choices);
}
void settings_add_int_module(const char *module, const char *section,
@ -218,7 +272,7 @@ void settings_add_int_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_int = def;
settings_add(module, section, key, SETTING_TYPE_INT, &default_value);
settings_add(module, section, key, SETTING_TYPE_INT, &default_value, NULL);
}
void settings_add_bool_module(const char *module, const char *section,
@ -228,8 +282,7 @@ void settings_add_bool_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_bool = def;
settings_add(module, section, key, SETTING_TYPE_BOOLEAN,
&default_value);
settings_add(module, section, key, SETTING_TYPE_BOOLEAN, &default_value, NULL);
}
void settings_add_time_module(const char *module, const char *section,
@ -239,7 +292,7 @@ void settings_add_time_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
settings_add(module, section, key, SETTING_TYPE_TIME, &default_value);
settings_add(module, section, key, SETTING_TYPE_TIME, &default_value, NULL);
}
void settings_add_level_module(const char *module, const char *section,
@ -249,7 +302,7 @@ void settings_add_level_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value);
settings_add(module, section, key, SETTING_TYPE_LEVEL, &default_value, NULL);
}
void settings_add_size_module(const char *module, const char *section,
@ -259,14 +312,16 @@ void settings_add_size_module(const char *module, const char *section,
memset(&default_value, 0, sizeof(default_value));
default_value.v_string = g_strdup(def);
settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value);
settings_add(module, section, key, SETTING_TYPE_SIZE, &default_value, NULL);
}
static void settings_destroy(SETTINGS_REC *rec)
{
if (rec->type != SETTING_TYPE_INT &&
rec->type != SETTING_TYPE_BOOLEAN)
rec->type != SETTING_TYPE_BOOLEAN &&
rec->type != SETTING_TYPE_CHOICE)
g_free(rec->default_value.v_string);
g_strfreev(rec->choices);
g_free(rec->module);
g_free(rec->section);
g_free(rec->key);
@ -328,6 +383,20 @@ static CONFIG_NODE *settings_get_node(const char *key)
return iconfig_node_section(node, rec->module, NODE_TYPE_BLOCK);
}
gboolean settings_set_choice(const char *key, const char *value)
{
SETTINGS_REC *rec;
rec = settings_get_record(key);
if (rec != NULL && strarray_find(rec->choices, value) < 0)
return FALSE;
settings_set_str(key, value);
return TRUE;
}
void settings_set_str(const char *key, const char *value)
{
iconfig_node_set_str(settings_get_node(key), key, value);
@ -343,7 +412,7 @@ void settings_set_bool(const char *key, int value)
iconfig_node_set_bool(settings_get_node(key), key, value);
}
int settings_set_time(const char *key, const char *value)
gboolean settings_set_time(const char *key, const char *value)
{
int msecs;
@ -354,7 +423,7 @@ int settings_set_time(const char *key, const char *value)
return TRUE;
}
int settings_set_level(const char *key, const char *value)
gboolean settings_set_level(const char *key, const char *value)
{
int iserror;
@ -366,7 +435,7 @@ int settings_set_level(const char *key, const char *value)
return TRUE;
}
int settings_set_size(const char *key, const char *value)
gboolean settings_set_size(const char *key, const char *value)
{
int size;
@ -683,7 +752,7 @@ static void init_configfile(void)
if (stat(get_irssi_dir(), &statbuf) != 0) {
/* ~/.irssi not found, create it. */
if (mkpath(get_irssi_dir(), 0700) != 0) {
if (g_mkdir_with_parents(get_irssi_dir(), 0700) != 0) {
g_error("Couldn't create %s directory", get_irssi_dir());
}
} else if (!S_ISDIR(statbuf.st_mode)) {

View File

@ -8,6 +8,7 @@ typedef enum {
SETTING_TYPE_TIME,
SETTING_TYPE_LEVEL,
SETTING_TYPE_SIZE,
SETTING_TYPE_CHOICE,
SETTING_TYPE_ANY
} SettingType;
@ -26,6 +27,7 @@ typedef struct {
SettingType type;
SettingValue default_value;
char **choices;
} SETTINGS_REC;
/* macros for handling the default Irssi configuration */
@ -58,6 +60,7 @@ 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 */
int settings_get_choice(const char *key);
char *settings_get_print(SETTINGS_REC *rec);
/* Functions to add/remove settings */
@ -73,6 +76,8 @@ 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_add_choice_module(const char *module, const char *section,
const char *key, int def, const char *choices);
void settings_remove(const char *key);
void settings_remove_module(const char *module);
@ -88,13 +93,16 @@ void settings_remove_module(const char *module);
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)
#define settings_add_choice(section, key, def, choices) \
settings_add_choice_module(MODULE_NAME, section, key, def, choices)
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);
gboolean settings_set_time(const char *key, const char *value);
gboolean settings_set_level(const char *key, const char *value);
gboolean settings_set_size(const char *key, const char *value);
gboolean settings_set_choice(const char *key, const char *value);
/* Get the type (SETTING_TYPE_xxx) of `key' */
SettingType settings_get_type(const char *key);

View File

@ -25,10 +25,7 @@
#include "settings.h"
#include "servers.h"
#include "misc.h"
#define ALIGN_RIGHT 0x01
#define ALIGN_CUT 0x02
#define ALIGN_PAD 0x04
#include "utf8.h"
#define isvarchar(c) \
(i_isalnum(c) || (c) == '_')
@ -47,7 +44,7 @@ static char *get_argument(char **cmd, char **arglist)
arg = 0;
max = -1;
argcount = arglist == NULL ? 0 : strarray_length(arglist);
argcount = arglist == NULL ? 0 : g_strv_length(arglist);
if (**cmd == '*') {
/* get all arguments */
@ -316,22 +313,28 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
}
/* return the aligned text */
static char *get_alignment(const char *text, int align, int flags, char pad)
char *get_alignment(const char *text, int align, int flags, char pad)
{
GString *str;
char *ret;
int policy;
unsigned int cut_bytes;
g_return_val_if_fail(text != NULL, NULL);
policy = string_policy(text);
str = g_string_new(text);
/* cut */
if ((flags & ALIGN_CUT) && align > 0 && str->len > align)
g_string_truncate(str, align);
if ((flags & ALIGN_CUT) && align > 0 && string_width(text, policy) > align) {
string_chars_for_width(text, policy, align, &cut_bytes);
g_string_truncate(str, cut_bytes);
}
/* add pad characters */
if (flags & ALIGN_PAD) {
while (str->len < align) {
while (string_width(str->str, policy) < align) {
if (flags & ALIGN_RIGHT)
g_string_prepend_c(str, pad);
else
@ -340,7 +343,7 @@ static char *get_alignment(const char *text, int align, int flags, char pad)
}
ret = str->str;
g_string_free(str, FALSE);
g_string_free(str, FALSE);
return ret;
}

View File

@ -9,9 +9,16 @@
#define PARSE_FLAG_ESCAPE_THEME 0x08 /* if any arguments/variables contain { or } chars, escape them with % */
#define PARSE_FLAG_ONLY_ARGS 0x10 /* expand only arguments ($0 $1 etc.) but no other $variables */
#define ALIGN_RIGHT 0x01
#define ALIGN_CUT 0x02
#define ALIGN_PAD 0x04
typedef char* (*SPECIAL_HISTORY_FUNC)
(const char *text, void *item, int *free_ret);
/* Cut and/or pad text so it takes exactly "align" characters on the screen */
char *get_alignment(const char *text, int align, int flags, char pad);
/* Parse and expand text after '$' character. return value has to be
g_free()'d if `free_ret' is TRUE. */
char *parse_special(char **cmd, SERVER_REC *server, void *item,

135
src/core/utf8.c Normal file
View File

@ -0,0 +1,135 @@
/* utf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 2002 Timo Sirainen
*
* Based on GLib code by
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "utf8.h"
#include "module.h"
/* Provide is_utf8(): */
#include "recode.h"
int string_advance(char const **str, int policy)
{
if (policy == TREAT_STRING_AS_UTF8) {
gunichar c;
c = g_utf8_get_char(*str);
*str = g_utf8_next_char(*str);
return unichar_isprint(c) ? mk_wcwidth(c) : 1;
} else {
/* Assume TREAT_STRING_AS_BYTES: */
*str += 1;
return 1;
}
}
int string_policy(const char *str)
{
if (is_utf8()) {
if (str == NULL || g_utf8_validate(str, -1, NULL)) {
/* No string provided or valid UTF-8 string: treat as UTF-8: */
return TREAT_STRING_AS_UTF8;
}
}
return TREAT_STRING_AS_BYTES;
}
int string_length(const char *str, int policy)
{
g_return_val_if_fail(str != NULL, 0);
if (policy == -1) {
policy = string_policy(str);
}
if (policy == TREAT_STRING_AS_UTF8) {
return g_utf8_strlen(str, -1);
}
else {
/* Assume TREAT_STRING_AS_BYTES: */
return strlen(str);
}
}
int string_width(const char *str, int policy)
{
int len;
g_return_val_if_fail(str != NULL, 0);
if (policy == -1) {
policy = string_policy(str);
}
len = 0;
while (*str != '\0') {
len += string_advance(&str, policy);
}
return len;
}
int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes)
{
const char *c, *previous_c;
int str_width, char_width, char_count;
g_return_val_if_fail(str != NULL, -1);
/* Handle the dummy case where n is 0: */
if (n == 0) {
if (bytes != NULL) {
*bytes = 0;
}
return 0;
}
if (policy == -1) {
policy = string_policy(str);
}
/* Iterate over characters until we reach n: */
char_count = 0;
str_width = 0;
c = str;
while (*c != '\0') {
previous_c = c;
char_width = string_advance(&c, policy);
if (str_width + char_width > n) {
/* We stepped beyond n, get one step back and stop there: */
c = previous_c;
break;
}
++ char_count;
str_width += char_width;
}
/* At this point, we know that char_count characters reach str_width
* columns, which is less than or equal to n. */
/* Optionally provide the equivalent amount of bytes: */
if (bytes != NULL) {
*bytes = c - str;
}
return char_count;
}

56
src/core/utf8.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef __UTF8_H
#define __UTF8_H
/* 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))
#include <glib.h>
typedef guint32 unichar;
/* Returns width for character (0-2). */
int mk_wcwidth(unichar c);
/* Advance the str pointer one character further; return the number of columns
* occupied by the skipped character.
*/
int string_advance(char const **str, int policy);
/* TREAT_STRING_AS_BYTES means strings are to be treated using strncpy,
* strnlen, etc.
* TREAT_STRING_AS_UTF8 means strings are to be treated using g_utf8_*
* functions.
*/
enum str_policy {
TREAT_STRING_AS_BYTES,
TREAT_STRING_AS_UTF8
};
/* Return how the str string ought to be treated: TREAT_STRING_AS_UTF8 if the
* terminal handles UTF-8 and if the string appears to be a valid UTF-8 string;
* TREAT_STRING_AS_BYTES otherwise.
*/
int string_policy(const char *str);
/* Return the length of the str string according to the given policy; if policy
* is -1, this function will call string_policy().
*/
int string_length(const char *str, int policy);
/* Return the screen width of the str string according to the given policy; if
* policy is -1, this function will call string_policy().
*/
int string_width(const char *str, int policy);
/* Return the amount of characters from str it takes to reach n columns, or -1 if
* str is NULL. Optionally return the equivalent amount of bytes.
* If policy is -1, this function will call string_policy().
*/
int string_chars_for_width(const char *str, int policy, unsigned int n, unsigned int *bytes);
#define unichar_isprint(c) (((c) & ~0x80) >= 32)
#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)
#endif

View File

@ -59,7 +59,7 @@
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*/
#include "module.h"
#include "utf8.h"
struct interval {
int first;

View File

@ -24,8 +24,6 @@ libfe_common_core_a_SOURCES = \
fe-queries.c \
fe-server.c \
fe-settings.c \
utf8.c \
wcwidth.c \
formats.c \
hilight-text.c \
keyboard.c \
@ -62,6 +60,3 @@ pkginc_fe_common_core_HEADERS = \
window-items.h \
windows-layout.h \
fe-windows.h
noinst_HEADERS = \
utf8.h

View File

@ -38,11 +38,18 @@
#include "chat-completion.h"
#include "window-items.h"
enum {
COMPLETE_MCASE_NEVER = 0,
COMPLETE_MCASE_ALWAYS,
COMPLETE_MCASE_AUTO,
};
static int keep_privates_count, keep_publics_count;
static int completion_lowercase;
static char *completion_char, *cmdchars;
static GSList *global_lastmsgs;
static int completion_auto, completion_strict;
static int completion_match_case;
#define SERVER_LAST_MSG_ADD(server, nick) \
last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \
@ -52,6 +59,18 @@ static int completion_auto, completion_strict;
last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \
nick, own, keep_publics_count)
static gboolean contains_uppercase(const char *s1)
{
const char *ch;
for (ch = s1; *ch != '\0'; ch++) {
if (g_ascii_isupper(*ch))
return TRUE;
}
return FALSE;
}
static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick)
{
while (list != NULL) {
@ -336,7 +355,8 @@ GList *completion_msg(SERVER_REC *win_server,
}
static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
const char *nick, const char *suffix)
const char *nick, const char *suffix,
const int match_case)
{
MODULE_CHANNEL_REC *mchannel;
GSList *tmp;
@ -352,8 +372,10 @@ static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) {
LAST_MSG_REC *rec = tmp->data;
if (g_ascii_strncasecmp(rec->nick, nick, len) == 0 &&
glist_find_icase_string(*outlist, rec->nick) == NULL) {
if ((match_case? strncmp(rec->nick, nick, len)
: g_ascii_strncasecmp(rec->nick, nick, len)) == 0 &&
(match_case? glist_find_string(*outlist, rec->nick)
: glist_find_icase_string(*outlist, rec->nick)) == NULL) {
str = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase) ascii_strdown(str);
if (rec->own)
@ -368,7 +390,8 @@ static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
const char *nick,
const char *suffix)
const char *suffix,
const int match_case)
{
GSList *nicks, *tmp;
GList *list;
@ -404,7 +427,8 @@ static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
*out = '\0';
/* add to list if 'cleaned' nick matches */
if (g_ascii_strncasecmp(str, nick, len) == 0) {
if ((match_case? strncmp(str, nick, len)
: g_ascii_strncasecmp(str, nick, len)) == 0) {
tnick = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase)
ascii_strdown(tnick);
@ -428,7 +452,7 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
GSList *nicks, *tmp;
GList *list;
char *str;
int len;
int len, match_case;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
@ -437,9 +461,12 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
if (suffix != NULL && *suffix == '\0')
suffix = NULL;
match_case = completion_match_case == COMPLETE_MCASE_ALWAYS ||
(completion_match_case == COMPLETE_MCASE_AUTO && contains_uppercase(nick));
/* put first the nicks who have recently said something */
list = NULL;
complete_from_nicklist(&list, channel, nick, suffix);
complete_from_nicklist(&list, channel, nick, suffix, match_case);
/* and add the rest of the nicks too */
len = strlen(nick);
@ -447,7 +474,8 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
if (g_ascii_strncasecmp(rec->nick, nick, len) == 0 &&
if ((match_case? strncmp(rec->nick, nick, len)
: g_ascii_strncasecmp(rec->nick, nick, len)) == 0 &&
rec != channel->ownnick) {
str = g_strconcat(rec->nick, suffix, NULL);
if (completion_lowercase)
@ -463,7 +491,7 @@ static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
/* remove non alphanum chars from nick and search again in case
list is still NULL ("foo<tab>" would match "_foo_" f.e.) */
if (!completion_strict)
list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix));
list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix, match_case));
return list;
}
@ -1130,6 +1158,8 @@ static void read_settings(void)
completion_auto = settings_get_bool("completion_auto");
completion_strict = settings_get_bool("completion_strict");
completion_match_case = settings_get_choice("completion_nicks_match_case");
g_free_not_null(completion_char);
completion_char = g_strdup(settings_get_str("completion_char"));
@ -1150,6 +1180,7 @@ void chat_completion_init(void)
settings_add_int("completion", "completion_keep_privates", 10);
settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
settings_add_bool("completion", "completion_strict", FALSE);
settings_add_choice("completion", "completion_nicks_match_case", COMPLETE_MCASE_AUTO, "never;always;auto");
settings_add_bool("lookandfeel", "expand_escapes", FALSE);

View File

@ -37,8 +37,11 @@ static int last_want_space, last_line_pos;
#define isseparator_notspace(c) \
((c) == ',')
#define isseparator_space(c) \
((c) == ' ')
#define isseparator(c) \
((c) == ' ' || isseparator_notspace(c))
(isseparator_space(c) || isseparator_notspace(c))
void chat_completion_init(void);
void chat_completion_deinit(void);
@ -153,20 +156,23 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
word = NULL;
linestart = NULL;
} else {
char* old_wordstart;
/* get the word we want to complete */
word = get_word_at(line, *pos, &wordstart);
old_wordstart = wordstart;
startpos = (int) (wordstart-line);
wordlen = strlen(word);
/* get the start of line until the word we're completing */
if (isseparator(*line)) {
/* empty space at the start of line */
if (wordstart == line)
wordstart += strlen(wordstart);
} else {
while (wordstart > line && isseparator(wordstart[-1]))
wordstart--;
}
/* remove trailing spaces from linestart */
while (wordstart > line && isseparator_space(wordstart[-1]))
wordstart--;
/* unless everything was spaces */
if (old_wordstart > line && wordstart == line)
wordstart = old_wordstart - 1;
linestart = g_strndup(line, (int) (wordstart-line));
/* completions usually add space after the word, that makes
@ -175,19 +181,24 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
BUT if we start completion with "/msg "<tab>, we don't
want to complete the /msg word, but instead complete empty
word with /msg being in linestart. */
if (!erase && *pos > 0 && line[*pos-1] == ' ' &&
(*linestart == '\0' || wordstart[-1] != ' ')) {
if (!erase && *pos > 0 && isseparator_space(line[*pos-1]) &&
(*linestart == '\0' || !isseparator_space(wordstart[-1]))) {
char *old;
old = linestart;
old = linestart;
linestart = *linestart == '\0' ?
g_strdup(word) :
g_strconcat(linestart, " ", word, NULL);
g_strdup_printf("%s%c%s",
/* do not accidentally duplicate the word separator */
line == wordstart - 1 ? "" : linestart,
wordstart[-1], word);
g_free(old);
g_free(word);
word = g_strdup("");
startpos = strlen(linestart)+1;
startpos = *linestart == '\0' ? 0 :
strlen(linestart)+1;
wordlen = 0;
}
@ -217,6 +228,11 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
want_space = TRUE;
signal_emit("complete word", 5, &complist, window, word, linestart, &want_space);
last_want_space = want_space;
if (complist != NULL) {
/* Remove all nulls (from the signal) before doing further processing */
complist = g_list_remove_all(g_list_first(complist), NULL);
}
}
g_free(linestart);
@ -329,7 +345,9 @@ GList *filename_complete(const char *path, const char *default_path)
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
continue; /* skip . and .. */
if (basename[0] != '.')
/* Skip the dotfiles unless the user explicitly asked us
* to do so. Basename might be './', beware of that */
if (basename[0] != '.' || basename[1] == '\0')
continue;
}
@ -392,7 +410,8 @@ static GList *completion_get_aliases(const char *alias, char cmdchar)
continue;
if (g_ascii_strncasecmp(node->key, alias, len) == 0) {
word = g_strdup_printf("%c%s", cmdchar, node->key);
word = cmdchar == '\0' ? g_strdup(node->key) :
g_strdup_printf("%c%s", cmdchar, node->key);
/* add matching alias to completion list, aliases will
be appended after command completions and kept in
uppercase to show it's an alias */
@ -584,13 +603,19 @@ static void sig_complete_word(GList **list, WINDOW_REC *window,
/* command completion? */
cmdchars = settings_get_str("cmdchars");
if (*word != '\0' && *linestart == '\0' && strchr(cmdchars, *word)) {
if (*word != '\0' && ((*linestart == '\0' && strchr(cmdchars, *word)) ||
(*linestart != '\0' && linestart[1] == '\0' &&
strchr(cmdchars, *linestart)))) {
gboolean skip = *linestart == '\0' ? TRUE : FALSE;
/* complete /command */
*list = completion_get_commands(word+1, *word);
*list = completion_get_commands(word + (skip ? 1 : 0),
skip ? *word : '\0');
/* complete aliases, too */
*list = g_list_concat(*list,
completion_get_aliases(word+1, *word));
completion_get_aliases(word + (skip ? 1 : 0),
skip ? *word : '\0'));
if (*list != NULL) signal_stop();
return;
@ -686,8 +711,20 @@ static void sig_complete_set(GList **list, WINDOW_REC *window,
SETTINGS_REC *rec = settings_get_record(line);
if (rec != NULL) {
char *value = settings_get_print(rec);
/* show the current option first */
if (value != NULL)
*list = g_list_append(*list, value);
/* show the whole list of valid options */
if (rec->type == SETTING_TYPE_CHOICE) {
char **tmp;
for (tmp = rec->choices; *tmp; tmp++) {
if (g_ascii_strcasecmp(*tmp, value) != 0)
*list = g_list_append(*list, g_strdup(*tmp));
}
}
}
}

View File

@ -26,6 +26,8 @@
#include "levels.h"
#include "misc.h"
#include "settings.h"
#include "special-vars.h"
#include "utf8.h"
#include "chat-protocols.h"
#include "chatnets.h"
@ -246,9 +248,7 @@ static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
}
}
/* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
<channel> <network> [<password>] */
static void cmd_channel_add(const char *data)
static void cmd_channel_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
CHATNET_REC *chatnetrec;
@ -257,18 +257,19 @@ static void cmd_channel_add(const char *data)
void *free_arg;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
"channel add", &optlist, &channel, &chatnet, &password))
"channel add", &optlist, &channel, &chatnet, &password))
return;
if (*chatnet == '\0' || *channel == '\0')
if (*chatnet == '\0' || *channel == '\0') {
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
}
chatnetrec = chatnet_find(chatnet);
if (chatnetrec == NULL) {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_UNKNOWN_CHATNET, chatnet);
TXT_UNKNOWN_CHATNET, chatnet);
cmd_params_free(free_arg);
return;
return;
}
botarg = g_hash_table_lookup(optlist, "bots");
@ -276,6 +277,13 @@ static void cmd_channel_add(const char *data)
rec = channel_setup_find(channel, chatnet);
if (rec == NULL) {
if (add == FALSE) {
cmd_params_free(free_arg);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_CHANSETUP_NOT_FOUND, channel, chatnet);
return;
}
rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
rec->name = g_strdup(channel);
rec->chatnet = g_strdup(chatnet);
@ -299,6 +307,18 @@ static void cmd_channel_add(const char *data)
cmd_params_free(free_arg);
}
/* SYNTAX: CHANNEL ADD|MODIFY [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
<channel> <network> [<password>] */
static void cmd_channel_add(const char *data)
{
cmd_channel_add_modify(data, TRUE);
}
static void cmd_channel_modify(const char *data)
{
cmd_channel_add_modify(data, FALSE);
}
/* SYNTAX: CHANNEL REMOVE <channel> <network> */
static void cmd_channel_remove(const char *data)
{
@ -323,40 +343,40 @@ static void cmd_channel_remove(const char *data)
static int get_nick_length(void *data)
{
return strlen(((NICK_REC *) data)->nick);
return string_width(((NICK_REC *) data)->nick, -1);
}
static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
{
WINDOW_REC *window;
WINDOW_REC *window;
TEXT_DEST_REC dest;
GString *str;
GSList *tmp;
char *format, *stripped, *prefix_format;
char *linebuf, nickmode[2] = { 0, 0 };
char *format, *stripped, *prefix_format;
char *aligned_nick, nickmode[2] = { 0, 0 };
int *columns, cols, rows, last_col_rows, col, row, max_width;
int item_extra, linebuf_size, formatnum;
int item_extra, formatnum;
window = window_find_closest(channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP);
max_width = window->width;
MSGLEVEL_CLIENTCRAP);
max_width = window->width;
/* get the length of item extra stuff ("[ ] ") */
/* get the length of item extra stuff ("[ ] ") */
format = format_get_text(MODULE_NAME, NULL,
channel->server, channel->visible_name,
TXT_NAMES_NICK, " ", "");
channel->server, channel->visible_name,
TXT_NAMES_NICK, " ", "");
stripped = strip_codes(format);
item_extra = strlen(stripped);
g_free(stripped);
g_free(stripped);
g_free(format);
if (settings_get_int("names_max_width") > 0 &&
settings_get_int("names_max_width") < max_width)
max_width = settings_get_int("names_max_width");
/* remove width of the timestamp from max_width */
/* remove width of the timestamp from max_width */
format_create_dest(&dest, channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP, NULL);
MSGLEVEL_CLIENTCRAP, NULL);
format = format_get_line_start(current_theme, &dest, time(NULL));
if (format != NULL) {
stripped = strip_codes(format);
@ -365,11 +385,11 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
g_free(format);
}
/* remove width of the prefix from max_width */
/* remove width of the prefix from max_width */
prefix_format = format_get_text(MODULE_NAME, NULL,
channel->server, channel->visible_name,
TXT_NAMES_PREFIX,
channel->visible_name);
channel->server, channel->visible_name,
TXT_NAMES_PREFIX,
channel->visible_name);
if (prefix_format != NULL) {
stripped = strip_codes(prefix_format);
max_width -= strlen(stripped);
@ -384,19 +404,18 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
/* calculate columns */
cols = get_max_column_count(nicklist, get_nick_length, max_width,
settings_get_int("names_max_columns"),
item_extra, 3, &columns, &rows);
settings_get_int("names_max_columns"),
item_extra, 3, &columns, &rows);
nicklist = columns_sort_list(nicklist, rows);
/* rows in last column */
/* rows in last column */
last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
if (last_col_rows == 0)
last_col_rows = rows;
last_col_rows = rows;
str = g_string_new(prefix_format);
linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
col = 0; row = 0;
col = 0; row = 0;
for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
@ -405,48 +424,44 @@ static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
else
nickmode[0] = ' ';
if (linebuf_size < columns[col]-item_extra+1) {
linebuf_size = (columns[col]-item_extra+1)*2;
linebuf = g_realloc(linebuf, linebuf_size);
}
memset(linebuf, ' ', columns[col]-item_extra);
linebuf[columns[col]-item_extra] = '\0';
memcpy(linebuf, rec->nick, strlen(rec->nick));
aligned_nick = get_alignment(rec->nick,
columns[col]-item_extra,
ALIGN_PAD, ' ');
formatnum = rec->op ? TXT_NAMES_NICK_OP :
rec->halfop ? TXT_NAMES_NICK_HALFOP :
rec->voice ? TXT_NAMES_NICK_VOICE :
TXT_NAMES_NICK;
formatnum = rec->op ? TXT_NAMES_NICK_OP :
rec->halfop ? TXT_NAMES_NICK_HALFOP :
rec->voice ? TXT_NAMES_NICK_VOICE :
TXT_NAMES_NICK;
format = format_get_text(MODULE_NAME, NULL,
channel->server,
channel->visible_name,
formatnum, nickmode, linebuf);
channel->server,
channel->visible_name,
formatnum, nickmode, aligned_nick);
g_string_append(str, format);
g_free(aligned_nick);
g_free(format);
if (++col == cols) {
printtext(channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP, "%s", str->str);
MSGLEVEL_CLIENTCRAP, "%s", str->str);
g_string_truncate(str, 0);
if (prefix_format != NULL)
g_string_assign(str, prefix_format);
g_string_assign(str, prefix_format);
col = 0; row++;
if (row == last_col_rows)
cols--;
cols--;
}
}
if (str->len > strlen(prefix_format)) {
printtext(channel->server, channel->visible_name,
MSGLEVEL_CLIENTCRAP, "%s", str->str);
MSGLEVEL_CLIENTCRAP, "%s", str->str);
}
g_slist_free(nicklist);
g_string_free(str, TRUE);
g_free_not_null(columns);
g_free_not_null(prefix_format);
g_free(linebuf);
}
void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
@ -622,12 +637,14 @@ void fe_channels_init(void)
command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel);
command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add);
command_bind("channel modify", NULL, (SIGNAL_FUNC) cmd_channel_modify);
command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove);
command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list);
command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
command_set_options("channel add", "auto noauto -bots -botcmd");
command_set_options("channel modify", "auto noauto -bots -botcmd");
command_set_options("names", "count ops halfops voices normal");
command_set_options("join", "invite window");
}
@ -643,6 +660,7 @@ void fe_channels_deinit(void)
command_unbind("join", (SIGNAL_FUNC) cmd_join);
command_unbind("channel", (SIGNAL_FUNC) cmd_channel);
command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add);
command_unbind("channel modify", (SIGNAL_FUNC) cmd_channel_modify);
command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove);
command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list);
command_unbind("names", (SIGNAL_FUNC) cmd_names);

View File

@ -459,15 +459,24 @@ gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest)
{
g_return_val_if_fail(array != NULL, FALSE);
if (strarray_find(array, "*") != -1)
return TRUE;
if (strarray_find(array, dest->target) != -1)
return TRUE;
if (dest->server_tag != NULL) {
char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, "*");
int ret = strarray_find(array, tagtarget);
g_free(tagtarget);
if (ret != -1)
return TRUE;
tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target);
ret = strarray_find(array, tagtarget);
g_free(tagtarget);
if (ret != -1)
return TRUE;
}
return FALSE;
}

View File

@ -51,7 +51,8 @@ static int ret_texts[] = {
TXT_INVALID_TIME,
TXT_INVALID_CHARSET,
TXT_EVAL_MAX_RECURSE,
TXT_PROGRAM_NOT_FOUND
TXT_PROGRAM_NOT_FOUND,
TXT_NO_SERVER_DEFINED,
};
int command_hide_output;

View File

@ -237,22 +237,13 @@ static int signal_name_to_id(const char *name)
static int cmd_options_get_signal(const char *cmd,
GHashTable *optlist)
{
GSList *list, *tmp, *next;
GList *list;
char *signame;
int signum;
/* get all the options, then remove the known ones. there should
be only one left - the signal */
list = hashtable_get_keys(optlist);
if (cmd != NULL) {
for (tmp = list; tmp != NULL; tmp = next) {
char *option = tmp->data;
next = tmp->next;
if (command_have_option(cmd, option))
list = g_slist_remove(list, option);
}
}
list = optlist_remove_known(cmd, optlist);
if (list == NULL)
return -1;
@ -272,7 +263,7 @@ static int cmd_options_get_signal(const char *cmd,
return -2;
}
g_slist_free(list);
g_list_free(list);
return signum;
}

View File

@ -215,7 +215,7 @@ static void cmd_unignore(const char *data)
{
IGNORE_REC *rec;
GSList *tmp;
char *mask;
char *mask, *mask_orig;
void *free_arg;
if (!cmd_get_params(data, &free_arg, 1, &mask))
@ -224,6 +224,10 @@ static void cmd_unignore(const char *data)
if (*mask == '\0')
cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
/* Save the mask string here since it might be modified in the code
* below and we need it to print meaningful error messages. */
mask_orig = mask;
if (is_numeric(mask, ' ')) {
/* with index number */
tmp = g_slist_nth(ignores, atoi(mask)-1);
@ -248,7 +252,7 @@ static void cmd_unignore(const char *data)
ignore_update_rec(rec);
} else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_IGNORE_NOT_FOUND, mask);
TXT_IGNORE_NOT_FOUND, mask_orig);
}
cmd_params_free(free_arg);
}

View File

@ -453,7 +453,7 @@ static void autolog_open(SERVER_REC *server, const char *server_tag,
log_item_add(log, LOG_ITEM_TARGET, target, server_tag);
dir = g_path_get_dirname(log->real_fname);
mkpath(dir, log_dir_create_mode);
g_mkdir_with_parents(dir, log_dir_create_mode);
g_free(dir);
log->temp = TRUE;

View File

@ -175,6 +175,7 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
int for_me, print_channel, level;
char *nickmode, *color, *freemsg = NULL;
HILIGHT_REC *hilight;
TEXT_DEST_REC dest;
/* NOTE: this may return NULL if some channel is just closed with
/WINDOW CLOSE and server still sends the few last messages */
@ -214,7 +215,6 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
if (printnick == NULL)
printnick = nick;
TEXT_DEST_REC dest;
format_create_dest(&dest, server, target, level, NULL);
dest.address = address;
dest.nick = nick;
@ -396,8 +396,9 @@ static void sig_message_quit(SERVER_REC *server, const char *nick,
count = 0; windows = NULL;
chans = g_string_new(NULL);
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec;
level = MSGLEVEL_QUITS;
CHANNEL_REC *rec = tmp->data;
rec = tmp->data;
if (!nicklist_find(rec, nick))
continue;

View File

@ -104,7 +104,7 @@ static SERVER_SETUP_REC *create_server_setup(GHashTable *optlist)
return server;
}
static void cmd_server_add(const char *data)
static void cmd_server_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
SERVER_SETUP_REC *rec;
@ -113,7 +113,7 @@ static void cmd_server_add(const char *data)
int port;
if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
"server add", &optlist, &addr, &portstr, &password))
"server add", &optlist, &addr, &portstr, &password))
return;
if (*addr == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
@ -124,6 +124,13 @@ static void cmd_server_add(const char *data)
rec = server_setup_find(addr, port, chatnet);
if (rec == NULL) {
if (add == FALSE) {
cmd_params_free(free_arg);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_SETUPSERVER_NOT_FOUND, addr, port);
return;
}
rec = create_server_setup(optlist);
if (rec == NULL) {
cmd_params_free(free_arg);
@ -205,6 +212,16 @@ static void cmd_server_add(const char *data)
cmd_params_free(free_arg);
}
static void cmd_server_add(const char *data)
{
cmd_server_add_modify(data, TRUE);
}
static void cmd_server_modify(const char *data)
{
cmd_server_add_modify(data, FALSE);
}
/* SYNTAX: SERVER REMOVE <address> [<port>] [<network>] */
static void cmd_server_remove(const char *data)
{
@ -388,10 +405,12 @@ void fe_server_init(void)
command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
command_bind("server connect", NULL, (SIGNAL_FUNC) cmd_server_connect);
command_bind("server add", NULL, (SIGNAL_FUNC) cmd_server_add);
command_bind("server modify", NULL, (SIGNAL_FUNC) cmd_server_modify);
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 +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
@ -412,6 +431,7 @@ void fe_server_deinit(void)
command_unbind("server", (SIGNAL_FUNC) cmd_server);
command_unbind("server connect", (SIGNAL_FUNC) cmd_server_connect);
command_unbind("server add", (SIGNAL_FUNC) cmd_server_add);
command_unbind("server modify", (SIGNAL_FUNC) cmd_server_modify);
command_unbind("server remove", (SIGNAL_FUNC) cmd_server_remove);
command_unbind("server", (SIGNAL_FUNC) server_command);
command_unbind("disconnect", (SIGNAL_FUNC) server_command);

View File

@ -67,6 +67,7 @@ static void set_print_pattern(const char *pattern)
static void set_boolean(const char *key, const char *value)
{
char *stripped_value;
stripped_value = g_strdup(value);
g_strstrip(stripped_value);
@ -79,7 +80,7 @@ static void set_boolean(const char *key, const char *value)
else
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_NOT_TOGGLE);
g_free(stripped_value);
g_free(stripped_value);
}
static void set_int(const char *key, const char *value)
@ -99,6 +100,24 @@ static void set_int(const char *key, const char *value)
settings_set_int(key, (int)longval);
}
static void set_choice(const char *key, const char *value)
{
char *stripped_value;
stripped_value = g_strdup(value);
g_strstrip(stripped_value);
if (settings_set_choice(key, stripped_value) == FALSE) {
SETTINGS_REC *rec = settings_get_record(key);
char *msg = g_strjoinv(", ", rec->choices);
printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, TXT_INVALID_CHOICE, msg);
g_free(msg);
}
g_free(stripped_value);
}
/* SYNTAX: SET [-clear | -default] [<key> [<value>]] */
static void cmd_set(char *data)
{
@ -142,6 +161,12 @@ static void cmd_set(char *data)
else
set_int(key, value);
break;
case SETTING_TYPE_CHOICE:
if (clear || set_default)
settings_set_choice(key, rec->choices[rec->default_value.v_int]);
else
set_choice(key, value);
break;
case SETTING_TYPE_STRING:
settings_set_str(key, clear ? "" :
set_default ? rec->default_value.v_string :

View File

@ -422,33 +422,17 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
window_find_closest(server, target, level);
}
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 unichar_isprint(c) ? mk_wcwidth(c) : 1;
} else {
*str += 1;
return 1;
}
}
/* Return length of text part in string (ie. without % codes) */
int format_get_length(const char *str)
{
GString *tmp;
int len;
gboolean utf8;
int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
utf8 = string_policy(str);
tmp = g_string_new(NULL);
len = 0;
@ -467,7 +451,7 @@ int format_get_length(const char *str)
len++;
}
len += advance(&str, utf8);
len += string_advance(&str, utf8);
}
g_string_free(tmp, TRUE);
@ -482,12 +466,12 @@ int format_real_length(const char *str, int len)
GString *tmp;
const char *start;
const char *oldstr;
gboolean utf8;
int utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
g_return_val_if_fail(len >= 0, 0);
utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
utf8 = string_policy(str);
start = str;
tmp = g_string_new(NULL);
@ -509,7 +493,7 @@ int format_real_length(const char *str, int len)
}
oldstr = str;
len -= advance(&str, utf8);
len -= string_advance(&str, utf8);
if (len < 0)
str = oldstr;
}

View File

@ -49,7 +49,7 @@ static void reset_level_cache(void)
HILIGHT_REC *rec = tmp->data;
if (never_hilight_level & rec->level)
never_hilight_level &= ~rec->level;
never_hilight_level &= ~rec->level;
}
}
@ -68,17 +68,18 @@ static void hilight_add_config(HILIGHT_REC *rec)
node = iconfig_node_traverse("(hilights", TRUE);
node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
iconfig_node_set_str(node, "text", rec->text);
if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
if (rec->color) iconfig_node_set_str(node, "color", rec->color);
if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
iconfig_node_set_bool(node, "nick", rec->nick);
iconfig_node_set_bool(node, "word", rec->word);
if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);
iconfig_node_set_str(node, "text", rec->text);
if (rec->level > 0) iconfig_node_set_int(node, "level", rec->level);
if (rec->color) iconfig_node_set_str(node, "color", rec->color);
if (rec->act_color) iconfig_node_set_str(node, "act_color", rec->act_color);
if (rec->priority > 0) iconfig_node_set_int(node, "priority", rec->priority);
iconfig_node_set_bool(node, "nick", rec->nick);
iconfig_node_set_bool(node, "word", rec->word);
if (rec->nickmask) iconfig_node_set_bool(node, "mask", TRUE);
if (rec->fullword) iconfig_node_set_bool(node, "fullword", TRUE);
if (rec->regexp) iconfig_node_set_bool(node, "regexp", TRUE);
if (rec->case_sensitive) iconfig_node_set_bool(node, "matchcase", TRUE);
if (rec->servertag) iconfig_node_set_str(node, "servertag", rec->servertag);
if (rec->channels != NULL && *rec->channels != NULL) {
node = iconfig_node_section(node, "channels", NODE_TYPE_LIST);
@ -121,8 +122,11 @@ static void hilight_init_rec(HILIGHT_REC *rec)
{
#ifdef HAVE_REGEX_H
if (rec->regexp_compiled) regfree(&rec->preg);
rec->regexp_compiled = !rec->regexp ? FALSE :
regcomp(&rec->preg, rec->text, REG_EXTENDED|REG_ICASE) == 0;
if (!rec->regexp)
rec->regexp_compiled = FALSE;
else
rec->regexp_compiled = regcomp(&rec->preg, rec->text,
rec->case_sensitive ? REG_EXTENDED : (REG_EXTENDED|REG_ICASE)) == 0;
#endif
}
@ -174,7 +178,7 @@ static HILIGHT_REC *hilight_find(const char *text, char **channels)
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
if (strarray_length(channels) != strarray_length(rec->channels))
if (g_strv_length(channels) != g_strv_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
@ -191,7 +195,7 @@ static HILIGHT_REC *hilight_find(const char *text, char **channels)
}
static int hilight_match_text(HILIGHT_REC *rec, const char *text,
int *match_beg, int *match_end)
int *match_beg, int *match_end)
{
char *match;
@ -200,9 +204,9 @@ static int hilight_match_text(HILIGHT_REC *rec, const char *text,
regmatch_t rmatch[1];
if (rec->regexp_compiled &&
regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
regexec(&rec->preg, text, 1, rmatch, 0) == 0) {
if (rmatch[0].rm_so > 0 &&
match_beg != NULL && match_end != NULL) {
match_beg != NULL && match_end != NULL) {
*match_beg = rmatch[0].rm_so;
*match_end = rmatch[0].rm_eo;
}
@ -210,9 +214,15 @@ static int hilight_match_text(HILIGHT_REC *rec, const char *text,
}
#endif
} else {
match = rec->fullword ?
stristr_full(text, rec->text) :
stristr(text, rec->text);
if (rec->case_sensitive) {
match = rec->fullword ?
strstr_full(text, rec->text) :
strstr(text, rec->text);
} else {
match = rec->fullword ?
stristr_full(text, rec->text) :
stristr(text, rec->text);
}
if (match != NULL) {
if (match_beg != NULL && match_end != NULL) {
*match_beg = (int) (match-text);
@ -222,7 +232,7 @@ static int hilight_match_text(HILIGHT_REC *rec, const char *text,
}
}
return FALSE;
return FALSE;
}
#define hilight_match_level(rec, level) \
@ -267,14 +277,14 @@ HILIGHT_REC *hilight_match(SERVER_REC *server, const char *channel,
HILIGHT_REC *rec = tmp->data;
if (!rec->nickmask && hilight_match_level(rec, level) &&
hilight_match_channel(rec, channel) &&
(rec->servertag == NULL ||
(server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) &&
hilight_match_text(rec, str, match_beg, match_end))
hilight_match_channel(rec, channel) &&
(rec->servertag == NULL ||
(server != NULL && g_ascii_strcasecmp(rec->servertag, server->tag) == 0)) &&
hilight_match_text(rec, str, match_beg, match_end))
return rec;
}
return NULL;
return NULL;
}
static char *hilight_get_act_color(HILIGHT_REC *rec)
@ -308,7 +318,7 @@ void hilight_update_text_dest(TEXT_DEST_REC *dest, HILIGHT_REC *rec)
g_free_and_null(dest->hilight_color);
if (rec->act_color != NULL && g_strcmp0(rec->act_color, "%n") == 0)
dest->level |= MSGLEVEL_NO_ACT;
else
else
dest->hilight_color = hilight_get_act_color(rec);
}
@ -362,29 +372,29 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
/* hilight whole line */
char *tmp = strip_codes(text);
newstr = g_strconcat(color, tmp, NULL);
g_free(tmp);
g_free(tmp);
} else {
/* hilight part of the line */
GString *tmp;
char *middle;
GString *tmp;
char *middle;
int pos, color_pos, color_len;
tmp = g_string_new(NULL);
tmp = g_string_new(NULL);
/* start of the line */
/* start of the line */
pos = strip_real_length(text, hilight_start, NULL, NULL);
g_string_append(tmp, text);
g_string_truncate(tmp, pos);
g_string_truncate(tmp, pos);
/* color */
g_string_append(tmp, color);
g_string_append(tmp, color);
/* middle of the line, stripped */
middle = strip_codes(text+pos);
pos = tmp->len;
pos = tmp->len;
g_string_append(tmp, middle);
g_string_truncate(tmp, pos+hilight_len);
g_free(middle);
g_string_truncate(tmp, pos+hilight_len);
g_free(middle);
/* end of the line */
pos = strip_real_length(text, hilight_end,
@ -398,8 +408,8 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text,
}
g_string_append(tmp, text+pos);
newstr = tmp->str;
g_string_free(tmp, FALSE);
newstr = tmp->str;
g_string_free(tmp, FALSE);
}
signal_emit("print text", 3, dest, newstr, stripped);
@ -417,7 +427,7 @@ HILIGHT_REC *hilight_match_nick(SERVER_REC *server, const char *channel,
HILIGHT_REC *rec;
rec = hilight_match(server, channel, nick, address,
level, msg, NULL, NULL);
level, msg, NULL, NULL);
return (rec == NULL || !rec->nick) ? NULL : rec;
}
@ -464,6 +474,7 @@ static void read_hilight_config(void)
rec->priority = config_node_get_int(node, "priority", 0);
rec->nick = config_node_get_bool(node, "nick", TRUE);
rec->word = config_node_get_bool(node, "word", TRUE);
rec->case_sensitive = config_node_get_bool(node, "matchcase", FALSE);
rec->nickmask = config_node_get_bool(node, "mask", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
@ -475,7 +486,7 @@ static void read_hilight_config(void)
if (node != NULL) rec->channels = config_node_get_list(node);
}
reset_cache();
reset_cache();
}
static void hilight_print(int index, HILIGHT_REC *rec)
@ -495,6 +506,7 @@ static void hilight_print(int index, HILIGHT_REC *rec)
if (rec->nickmask) g_string_append(options, "-mask ");
if (rec->fullword) g_string_append(options, "-full ");
if (rec->case_sensitive) g_string_append(options, "-matchcase ");
if (rec->regexp) {
g_string_append(options, "-regexp ");
#ifdef HAVE_REGEX_H
@ -519,10 +531,10 @@ static void hilight_print(int index, HILIGHT_REC *rec)
if (levelstr != NULL)
levelstr = g_strconcat(levelstr, " ", NULL);
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP,
TXT_HILIGHT_LINE, index, rec->text,
chans != NULL ? chans : "",
levelstr != NULL ? levelstr : "",
options->str);
TXT_HILIGHT_LINE, index, rec->text,
chans != NULL ? chans : "",
levelstr != NULL ? levelstr : "",
options->str);
g_free_not_null(chans);
g_free_not_null(levelstr);
g_string_free(options, TRUE);
@ -543,12 +555,12 @@ static void cmd_hilight_show(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_HILIGHT_FOOTER);
}
/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -regexp]
/* SYNTAX: HILIGHT [-nick | -word | -line] [-mask | -full | -matchcase | -regexp]
[-color <color>] [-actcolor <color>] [-level <level>]
[-network <network>] [-channels <channels>] <text> */
static void cmd_hilight(const char *data)
{
GHashTable *optlist;
GHashTable *optlist;
HILIGHT_REC *rec;
char *colorarg, *actcolorarg, *levelarg, *priorityarg, *chanarg, *text, *servertag;
char **channels;
@ -562,7 +574,7 @@ static void cmd_hilight(const char *data)
}
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
PARAM_FLAG_GETREST, "hilight", &optlist, &text))
PARAM_FLAG_GETREST, "hilight", &optlist, &text))
return;
chanarg = g_hash_table_lookup(optlist, "channels");
@ -582,7 +594,7 @@ static void cmd_hilight(const char *data)
rec = g_new0(HILIGHT_REC, 1);
/* default to nick/word hilighting */
rec->nick = TRUE;
rec->nick = TRUE;
rec->word = TRUE;
rec->text = g_strdup(text);
@ -602,15 +614,16 @@ static void cmd_hilight(const char *data)
if (g_hash_table_lookup(optlist, "word") != NULL) {
rec->word = TRUE;
rec->nick = FALSE;
rec->nick = FALSE;
}
if (g_hash_table_lookup(optlist, "nick") != NULL)
rec->nick = TRUE;
rec->nick = TRUE;
rec->nickmask = g_hash_table_lookup(optlist, "mask") != NULL;
rec->fullword = g_hash_table_lookup(optlist, "full") != NULL;
rec->regexp = g_hash_table_lookup(optlist, "regexp") != NULL;
rec->case_sensitive = g_hash_table_lookup(optlist, "matchcase") != NULL;
if (colorarg != NULL) {
g_free_and_null(rec->color);
@ -631,7 +644,7 @@ static void cmd_hilight(const char *data)
hilight_create(rec);
hilight_print(g_slist_index(hilights, rec)+1, rec);
cmd_params_free(free_arg);
cmd_params_free(free_arg);
reset_cache();
}
@ -649,7 +662,7 @@ static void cmd_dehilight(const char *data)
} else {
/* with mask */
char *chans[2] = { "*", NULL };
rec = hilight_find(data, chans);
rec = hilight_find(data, chans);
}
if (rec == NULL)
@ -657,20 +670,20 @@ static void cmd_dehilight(const char *data)
else {
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_HILIGHT_REMOVED, rec->text);
hilight_remove(rec);
reset_cache();
reset_cache();
}
}
static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
NICK_REC *nick)
NICK_REC *nick)
{
GSList *tmp;
HILIGHT_REC *match;
char *nickmask;
char *nickmask;
int len, best_match;
if (nick->host == NULL)
return; /* don't check until host is known */
return; /* don't check until host is known */
nickmask = g_strconcat(nick->nick, "!", nick->host, NULL);
@ -679,8 +692,8 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
HILIGHT_REC *rec = tmp->data;
if (rec->nickmask &&
hilight_match_channel(rec, channel->name) &&
match_wildcards(rec->text, nickmask)) {
hilight_match_channel(rec, channel->name) &&
match_wildcards(rec->text, nickmask)) {
len = strlen(rec->text);
if (best_match < len) {
best_match = len;
@ -691,7 +704,7 @@ static void hilight_nick_cache(GHashTable *list, CHANNEL_REC *channel,
g_free_not_null(nickmask);
if (match != NULL)
g_hash_table_insert(list, nick, match);
g_hash_table_insert(list, nick, match);
}
static void read_settings(void)
@ -706,28 +719,28 @@ void hilight_text_init(void)
settings_add_str("lookandfeel", "hilight_act_color", "%M");
settings_add_level("lookandfeel", "hilight_level", "PUBLIC DCCMSGS");
read_settings();
read_settings();
nickmatch = nickmatch_init(hilight_nick_cache);
read_hilight_config();
signal_add_first("print text", (SIGNAL_FUNC) sig_print_text);
signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight);
command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight);
command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp");
command_set_options("hilight", "-color -actcolor -level -priority -network -channels nick word line mask full regexp matchcase");
}
void hilight_text_deinit(void)
{
hilights_destroy_all();
nickmatch_deinit(nickmatch);
nickmatch_deinit(nickmatch);
signal_remove("print text", (SIGNAL_FUNC) sig_print_text);
signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight);
command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight);

View File

@ -23,6 +23,7 @@ struct _HILIGHT_REC {
unsigned int nickmask:1; /* `text' is a nick mask */
unsigned int fullword:1; /* match `text' only for full words */
unsigned int regexp:1; /* `text' is a regular expression */
unsigned int case_sensitive:1;/* `text' must match case */
#ifdef HAVE_REGEX_H
unsigned int regexp_compiled:1; /* should always be TRUE, unless regexp is invalid */
regex_t preg;

View File

@ -221,8 +221,10 @@ FORMAT_REC fecommon_core_formats[] = {
{ "invalid_level", "Invalid message level", 0 },
{ "invalid_size", "Invalid size", 0 },
{ "invalid_charset", "Invalid charset: $0", 1, { 0 } },
{ "invalid_choice", "Invalid choice, must be one of $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 },
{ "no_server_defined", "No servers defined for this network, see /help server for how to add one", 0 },
/* ---- */
{ NULL, "Themes", 0 },

View File

@ -190,8 +190,10 @@ enum {
TXT_INVALID_LEVEL,
TXT_INVALID_SIZE,
TXT_INVALID_CHARSET,
TXT_INVALID_CHOICE,
TXT_EVAL_MAX_RECURSE,
TXT_PROGRAM_NOT_FOUND,
TXT_NO_SERVER_DEFINED,
TXT_FILL_11,

View File

@ -2,7 +2,7 @@
#define MODULE_NAME "fe-common/core"
typedef guint32 unichar;
#include "utf8.h"
typedef struct {
time_t time;
char *nick;

View File

@ -365,9 +365,9 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
GString *str;
char *ret;
theme_rm_col dummy, reset;
int braces = 1; /* we start with one brace opened */
dummy.m[0] = '\0';
strcpy(reset.m, "n");
int braces = 1; /* we start with one brace opened */
str = g_string_new(NULL);
while (**format != '\0' && braces != 0) {

View File

@ -1,26 +0,0 @@
/* utf8.c - Operations on UTF-8 strings.
*
* Copyright (C) 2002 Timo Sirainen
*
* Based on GLib code by
*
* Copyright (C) 1999 Tom Tromey
* Copyright (C) 2000 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"

View File

@ -1,17 +0,0 @@
#ifndef __UTF8_H
#define __UTF8_H
/* 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 mk_wcwidth(unichar c);
#define unichar_isprint(c) (((c) & ~0x80) >= 32)
#define is_utf8_leading(c) (((c) & 0xc0) != 0x80)
#endif

View File

@ -50,12 +50,13 @@ const char *get_visible_target(IRC_SERVER_REC *server, const char *target)
return target;
}
/* SYNTAX: SERVER ADD [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
[-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
[-ssl_ciphers <list>]
[-auto | -noauto] [-network <network>] [-host <hostname>]
[-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
<address> [<port> [<password>]] */
/* SYNTAX: SERVER ADD|MODIFY [-4 | -6] [-ssl] [-ssl_cert <cert>] [-ssl_pkey <pkey>] [-ssl_pass <password>]
[-ssl_verify] [-ssl_cafile <cafile>] [-ssl_capath <capath>]
[-ssl_ciphers <list>]
[-auto | -noauto] [-network <network>] [-host <hostname>]
[-cmdspeed <ms>] [-cmdmax <count>] [-port <port>]
<address> [<port> [<password>]] */
/* NOTE: -network replaces the old -ircnet flag. */
static void sig_server_add_fill(IRC_SERVER_SETUP_REC *rec,
GHashTable *optlist)

View File

@ -88,14 +88,7 @@ static void cmd_network_list(void)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NETWORK_FOOTER);
}
/* SYNTAX: NETWORK ADD [-nick <nick>] [-user <user>] [-realname <name>]
[-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
[-querychans <count>] [-whois <count>] [-msgs <count>]
[-kicks <count>] [-modes <count>] [-cmdspeed <ms>]
[-cmdmax <count>] [-sasl_mechanism <mechanism>]
[-sasl_username <username>] [-sasl_password <password>]
<name> */
static void cmd_network_add(const char *data)
static void cmd_network_add_modify(const char *data, gboolean add)
{
GHashTable *optlist;
char *name, *value;
@ -103,12 +96,20 @@ static void cmd_network_add(const char *data)
IRC_CHATNET_REC *rec;
if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
"network add", &optlist, &name))
"network add", &optlist, &name))
return;
if (*name == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
rec = ircnet_find(name);
if (rec == NULL) {
if (add == FALSE) {
cmd_params_free(free_arg);
printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
IRCTXT_NETWORK_NOT_FOUND, name);
return;
}
rec = g_new0(IRC_CHATNET_REC, 1);
rec->name = g_strdup(name);
} else {
@ -174,6 +175,23 @@ static void cmd_network_add(const char *data)
cmd_params_free(free_arg);
}
/* SYNTAX: NETWORK ADD|MODIFY [-nick <nick>] [-user <user>] [-realname <name>]
[-host <host>] [-usermode <mode>] [-autosendcmd <cmd>]
[-querychans <count>] [-whois <count>] [-msgs <count>]
[-kicks <count>] [-modes <count>] [-cmdspeed <ms>]
[-cmdmax <count>] [-sasl_mechanism <mechanism>]
[-sasl_username <username>] [-sasl_password <password>]
<name> */
static void cmd_network_add(const char *data)
{
cmd_network_add_modify(data, TRUE);
}
static void cmd_network_modify(const char *data)
{
cmd_network_add_modify(data, FALSE);
}
/* SYNTAX: NETWORK REMOVE <network> */
static void cmd_network_remove(const char *data)
{
@ -206,10 +224,13 @@ void fe_ircnet_init(void)
command_bind("network", NULL, (SIGNAL_FUNC) cmd_network);
command_bind("network list", NULL, (SIGNAL_FUNC) cmd_network_list);
command_bind("network add", NULL, (SIGNAL_FUNC) cmd_network_add);
command_bind("network modify", NULL, (SIGNAL_FUNC) cmd_network_modify);
command_bind("network remove", NULL, (SIGNAL_FUNC) cmd_network_remove);
command_set_options("network add", "-kicks -msgs -modes -whois -cmdspeed "
"-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
command_set_options("network modify", "-kicks -msgs -modes -whois -cmdspeed "
"-cmdmax -nick -user -realname -host -autosendcmd -querychans -usermode -sasl_mechanism -sasl_username -sasl_password");
}
void fe_ircnet_deinit(void)
@ -218,5 +239,6 @@ void fe_ircnet_deinit(void)
command_unbind("network", (SIGNAL_FUNC) cmd_network);
command_unbind("network list", (SIGNAL_FUNC) cmd_network_list);
command_unbind("network add", (SIGNAL_FUNC) cmd_network_add);
command_unbind("network modify", (SIGNAL_FUNC) cmd_network_modify);
command_unbind("network remove", (SIGNAL_FUNC) cmd_network_remove);
}

View File

@ -4,8 +4,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/core/ \
-I$(top_srcdir)/src/fe-common/core/ \
$(GLIB_CFLAGS) \
$(CURSES_INCLUDEDIR)
$(GLIB_CFLAGS)
irssi_DEPENDENCIES = \
@COMMON_LIBS@ \
@ -22,25 +21,11 @@ irssi_LDADD = \
@PROG_LIBS@ \
@TEXTUI_LIBS@
tparm_sources = \
tparm.c
terminfo_sources = \
term-terminfo.c \
terminfo-core.c
curses_sources = \
term-curses.c
if NEED_TPARM
use_tparm_sources = $(tparm_sources)
endif
if USE_CURSES
use_term_sources = $(curses_sources)
else
use_term_sources = $(terminfo_sources)
endif
irssi_SOURCES = \
gui-entry.c \
@ -56,8 +41,6 @@ irssi_SOURCES = \
statusbar-config.c \
statusbar-items.c \
term.c \
term-dummy.c \
$(use_tparm_sources) \
$(use_term_sources) \
textbuffer.c \
textbuffer-commands.c \
@ -85,6 +68,4 @@ noinst_HEADERS = \
module-formats.h
EXTRA_DIST = \
$(tparm_sources) \
$(terminfo_sources) \
$(curses_sources)
$(terminfo_sources)

View File

@ -32,6 +32,8 @@
#undef i_tolower
#undef i_isalnum
#define KILL_RING_MAX 10
static unichar i_toupper(unichar c)
{
if (term_type == TERM_TYPE_UTF8)
@ -82,11 +84,22 @@ GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
void gui_entry_destroy(GUI_ENTRY_REC *entry)
{
GSList *tmp;
g_return_if_fail(entry != NULL);
if (active_entry == entry)
gui_entry_set_active(NULL);
for (tmp = entry->kill_ring; tmp != NULL; tmp = tmp->next) {
GUI_ENTRY_CUTBUFFER_REC *rec = tmp->data;
if (rec != NULL) {
g_free(rec->cutbuffer);
g_free(rec);
}
}
g_slist_free(entry->kill_ring);
g_free(entry->text);
g_free(entry->prompt);
g_free(entry);
@ -353,22 +366,8 @@ static int scrlen_str(const char *str)
char *stripped;
g_return_val_if_fail(str != NULL, 0);
str = stripped = strip_codes(str);
if (is_utf8() && g_utf8_validate(str, -1, NULL)) {
while (*str != '\0') {
gunichar c;
c = g_utf8_get_char(str);
str = g_utf8_next_char(str);
len += unichar_isprint(c) ? mk_wcwidth(c) : 1;
}
} else {
len = strlen(str);
}
stripped = strip_codes(str);
len = string_width(stripped, -1);
g_free(stripped);
return len;
}
@ -541,28 +540,51 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr)
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry)
{
GUI_ENTRY_CUTBUFFER_REC *tmp;
char *buf;
int i;
g_return_val_if_fail(entry != NULL, NULL);
if (entry->cutbuffer == NULL)
if (entry->kill_ring == NULL || entry->kill_ring->data == NULL)
return NULL;
tmp = entry->kill_ring->data;
if (tmp->cutbuffer == NULL)
return NULL;
if (entry->utf8)
buf = g_ucs4_to_utf8(entry->cutbuffer, -1, NULL, NULL, NULL);
buf = g_ucs4_to_utf8(tmp->cutbuffer, -1, NULL, NULL, NULL);
else {
buf = g_malloc(entry->cutbuffer_len*6 + 1);
buf = g_malloc(tmp->cutbuffer_len*6 + 1);
if (term_type == TERM_TYPE_BIG5)
unichars_to_big5(entry->cutbuffer, buf);
unichars_to_big5(tmp->cutbuffer, buf);
else
for (i = 0; i <= entry->cutbuffer_len; i++)
buf[i] = entry->cutbuffer[i];
for (i = 0; i <= tmp->cutbuffer_len; i++)
buf[i] = tmp->cutbuffer[i];
}
return buf;
}
void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry)
{
GUI_ENTRY_CUTBUFFER_REC *tmp;
g_return_val_if_fail(entry != NULL, NULL);
if (entry->kill_ring == NULL)
return NULL;
tmp = entry->kill_ring->data;
entry->kill_ring = g_slist_remove(entry->kill_ring, tmp);
entry->kill_ring = g_slist_append(entry->kill_ring, tmp);
return gui_entry_get_cutbuffer(entry);
}
void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
int newpos, size = 0;
@ -573,7 +595,41 @@ void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, int update_cutbuffer)
gui_entry_erase(entry, size, update_cutbuffer);
}
void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
GUI_ENTRY_CUTBUFFER_REC *tmp;
g_return_val_if_fail(entry != NULL, NULL);
if (entry->kill_ring == NULL) {
/* no kill ring exists */
entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
} else {
tmp = entry->kill_ring->data;
if (tmp != NULL && tmp->cutbuffer_len > 0
&& (!entry->previous_append_next_kill
|| update_cutbuffer == CUTBUFFER_UPDATE_REPLACE)) {
/* a cutbuffer exists and should be replaced */
entry->kill_ring = g_slist_prepend(entry->kill_ring, (void *)NULL);
}
}
if (g_slist_length(entry->kill_ring) > KILL_RING_MAX) {
GUI_ENTRY_CUTBUFFER_REC *rec = g_slist_last(entry->kill_ring)->data;
entry->kill_ring = g_slist_remove(entry->kill_ring, rec);
if (rec != NULL) g_free(rec->cutbuffer);
g_free(rec);
}
if (entry->kill_ring->data == NULL) {
entry->kill_ring->data = g_new0(GUI_ENTRY_CUTBUFFER_REC, 1);
}
return entry->kill_ring->data;
}
void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer)
{
size_t w = 0;
@ -582,17 +638,59 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
if (size == 0 || entry->pos < size)
return;
if (update_cutbuffer) {
/* put erased text to cutbuffer */
if (entry->cutbuffer_len < size) {
g_free(entry->cutbuffer);
entry->cutbuffer = g_new(unichar, size+1);
if (update_cutbuffer != CUTBUFFER_UPDATE_NOOP) {
int cutbuffer_new_size;
unichar *tmpcutbuffer;
GUI_ENTRY_CUTBUFFER_REC *tmp = get_cutbuffer_rec(entry, update_cutbuffer);
if (tmp->cutbuffer_len == 0) {
update_cutbuffer = CUTBUFFER_UPDATE_REPLACE;
}
entry->cutbuffer_len = size;
entry->cutbuffer[size] = '\0';
memcpy(entry->cutbuffer, entry->text + entry->pos - size,
size * sizeof(unichar));
cutbuffer_new_size = tmp->cutbuffer_len + size;
tmpcutbuffer = tmp->cutbuffer;
entry->append_next_kill = TRUE;
switch (update_cutbuffer) {
case CUTBUFFER_UPDATE_APPEND:
tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
memcpy(tmp->cutbuffer, tmpcutbuffer,
tmp->cutbuffer_len * sizeof(unichar));
memcpy(tmp->cutbuffer + tmp->cutbuffer_len,
entry->text + entry->pos - size, size * sizeof(unichar));
tmp->cutbuffer_len = cutbuffer_new_size;
tmp->cutbuffer[cutbuffer_new_size] = '\0';
g_free(tmpcutbuffer);
break;
case CUTBUFFER_UPDATE_PREPEND:
tmp->cutbuffer = g_new(unichar, cutbuffer_new_size+1);
memcpy(tmp->cutbuffer, entry->text + entry->pos - size,
size * sizeof(unichar));
memcpy(tmp->cutbuffer + size, tmpcutbuffer,
tmp->cutbuffer_len * sizeof(unichar));
tmp->cutbuffer_len = cutbuffer_new_size;
tmp->cutbuffer[cutbuffer_new_size] = '\0';
g_free(tmpcutbuffer);
break;
case CUTBUFFER_UPDATE_REPLACE:
/* put erased text to cutbuffer */
if (tmp->cutbuffer_len < size) {
g_free(tmp->cutbuffer);
tmp->cutbuffer = g_new(unichar, size+1);
}
tmp->cutbuffer_len = size;
tmp->cutbuffer[size] = '\0';
memcpy(tmp->cutbuffer, entry->text + entry->pos - size, size * sizeof(unichar));
break;
case CUTBUFFER_UPDATE_NOOP:
/* cannot happen, handled in "if" */
break;
}
}
if (entry->utf8)
@ -629,7 +727,7 @@ void gui_entry_erase_cell(GUI_ENTRY_REC *entry)
gui_entry_draw(entry);
}
void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to;
@ -652,10 +750,10 @@ void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space)
}
if (to > 0) to++;
gui_entry_erase(entry, entry->pos-to, TRUE);
gui_entry_erase(entry, entry->pos-to, cutbuffer_op);
}
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op)
{
int to, size;
@ -678,7 +776,7 @@ void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
size = to-entry->pos;
entry->pos = to;
gui_entry_erase(entry, size, TRUE);
gui_entry_erase(entry, size, cutbuffer_op);
}
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)

View File

@ -1,12 +1,16 @@
#ifndef __GUI_ENTRY_H
#define __GUI_ENTRY_H
typedef struct {
int cutbuffer_len;
unichar *cutbuffer;
} GUI_ENTRY_CUTBUFFER_REC;
typedef struct {
int text_len, text_alloc; /* as shorts, not chars */
unichar *text;
int cutbuffer_len;
unichar *cutbuffer;
GSList *kill_ring;
/* all as shorts, not chars */
int xpos, ypos, width; /* entry position in screen */
@ -18,8 +22,19 @@ typedef struct {
int redraw_needed_from;
unsigned int utf8:1;
unsigned int previous_append_next_kill:1;
unsigned int append_next_kill:1;
unsigned int yank_preceded:1;
} GUI_ENTRY_REC;
typedef enum {
CUTBUFFER_UPDATE_NOOP,
CUTBUFFER_UPDATE_REPLACE,
CUTBUFFER_UPDATE_APPEND,
CUTBUFFER_UPDATE_PREPEND
} CUTBUFFER_UPDATE_OP;
extern GUI_ENTRY_REC *active_entry;
GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8);
@ -40,11 +55,12 @@ 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);
char *gui_entry_get_next_cutbuffer(GUI_ENTRY_REC *entry);
void gui_entry_erase_to(GUI_ENTRY_REC *entry, int pos, CUTBUFFER_UPDATE_OP update_cutbuffer);
void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer);
void gui_entry_erase_cell(GUI_ENTRY_REC *entry);
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_erase_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPDATE_OP cutbuffer_op);
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry);
void gui_entry_transpose_words(GUI_ENTRY_REC *entry);
@ -60,4 +76,5 @@ void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space);
void gui_entry_redraw(GUI_ENTRY_REC *entry);
#endif

View File

@ -40,6 +40,9 @@
#include <string.h>
#include <signal.h>
/* After LINE_SPLIT_LIMIT characters, the message will be split into multiple lines */
#define LINE_SPLIT_LIMIT 400
typedef void (*ENTRY_REDIRECT_KEY_FUNC) (int key, void *data, SERVER_REC *server, WI_ITEM_REC *item);
typedef void (*ENTRY_REDIRECT_ENTRY_FUNC) (const char *line, void *data, SERVER_REC *server, WI_ITEM_REC *item);
@ -70,6 +73,7 @@ static int paste_timeout_id;
static int paste_use_bracketed_mode;
static int paste_bracketed_mode;
static int paste_was_bracketed_mode;
static int previous_yank_preceded;
/* Terminal sequences that surround the input when the terminal has the
* bracketed paste mode active. Fror more details see
@ -227,7 +231,7 @@ static void paste_buffer_join_lines(GArray *buf)
}
/* all looks fine - now remove the whitespace, but don't let lines
get longer than 400 chars */
get longer than LINE_SPLIT_LIMIT chars */
dest = arr; last_lf = TRUE; last_lf_pos = NULL; line_len = 0;
for (i = 0; i < buf->len; i++) {
if (last_lf && isblank(arr[i])) {
@ -245,7 +249,7 @@ static void paste_buffer_join_lines(GArray *buf)
last_lf = TRUE;
} else {
last_lf = FALSE;
if (++line_len >= 400 && last_lf_pos != NULL) {
if (++line_len >= LINE_SPLIT_LIMIT && last_lf_pos != NULL) {
memmove(last_lf_pos+1, last_lf_pos,
(dest - last_lf_pos) * sizeof(unichar));
*last_lf_pos = '\n'; last_lf_pos = NULL;
@ -357,11 +361,24 @@ static void insert_paste_prompt(void)
{
char *str;
/* The actual number of lines that will show up post-line-split */
int actual_line_count = paste_line_count;
int split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
/* in case this prompt is happening due to line-splitting, calculate the
number of lines obtained from this. The number isn't entirely accurate;
we just choose the greater of the two since the exact value isn't
important */
if (split_lines > paste_verify_line_count &&
split_lines > paste_line_count) {
actual_line_count = split_lines;
}
paste_prompt = TRUE;
paste_old_prompt = g_strdup(active_entry->prompt);
printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
TXT_PASTE_WARNING,
paste_line_count,
actual_line_count,
active_win->active == NULL ? "window" :
active_win->active->visible_name);
@ -424,11 +441,20 @@ static void sig_gui_key_pressed(gpointer keyp)
gui_entry_insert_char(active_entry, key);
ret = 1;
} else {
previous_yank_preceded = active_entry->yank_preceded;
active_entry->yank_preceded = FALSE;
active_entry->previous_append_next_kill = active_entry->append_next_kill;
active_entry->append_next_kill = FALSE;
ret = key_pressed(keyboard, str);
if (ret < 0) {
/* key wasn't used for anything, print it */
gui_entry_insert_char(active_entry, key);
}
if (ret == 0) {
/* combo not complete, ignore append_next_kill and yank_preceded */
active_entry->append_next_kill = active_entry->previous_append_next_kill;
active_entry->yank_preceded = previous_yank_preceded;
}
}
/* ret = 0 : some key create multiple characters - we're in the middle
@ -547,7 +573,7 @@ static void key_forward_to_space(void)
static void key_erase_line(void)
{
gui_entry_set_pos(active_entry, active_entry->text_len);
gui_entry_erase(active_entry, active_entry->text_len, TRUE);
gui_entry_erase(active_entry, active_entry->text_len, CUTBUFFER_UPDATE_REPLACE);
}
static void key_erase_to_beg_of_line(void)
@ -555,7 +581,7 @@ static void key_erase_to_beg_of_line(void)
int pos;
pos = gui_entry_get_pos(active_entry);
gui_entry_erase(active_entry, pos, TRUE);
gui_entry_erase(active_entry, pos, CUTBUFFER_UPDATE_PREPEND);
}
static void key_erase_to_end_of_line(void)
@ -564,7 +590,7 @@ static void key_erase_to_end_of_line(void)
pos = gui_entry_get_pos(active_entry);
gui_entry_set_pos(active_entry, active_entry->text_len);
gui_entry_erase(active_entry, active_entry->text_len - pos, TRUE);
gui_entry_erase(active_entry, active_entry->text_len - pos, CUTBUFFER_UPDATE_APPEND);
}
static void key_yank_from_cutbuffer(void)
@ -574,7 +600,36 @@ static void key_yank_from_cutbuffer(void)
cutbuffer = gui_entry_get_cutbuffer(active_entry);
if (cutbuffer != NULL) {
gui_entry_insert_text(active_entry, cutbuffer);
g_free(cutbuffer);
active_entry->yank_preceded = TRUE;
g_free(cutbuffer);
}
}
static void key_yank_next_cutbuffer(void)
{
GUI_ENTRY_CUTBUFFER_REC *rec;
guint length = 0;
char *cutbuffer;
if (!previous_yank_preceded) {
if (settings_get_bool("bell_beeps")) {
signal_emit("beep", 0);
}
return;
}
if (active_entry->kill_ring == NULL)
return;
rec = active_entry->kill_ring->data;
if (rec != NULL) length = rec->cutbuffer_len;
cutbuffer = gui_entry_get_next_cutbuffer(active_entry);
if (cutbuffer != NULL) {
gui_entry_erase(active_entry, length, CUTBUFFER_UPDATE_NOOP);
gui_entry_insert_text(active_entry, cutbuffer);
active_entry->yank_preceded = TRUE;
g_free(cutbuffer);
}
}
@ -611,34 +666,44 @@ static void key_delete_character(void)
static void key_backspace(void)
{
gui_entry_erase(active_entry, 1, FALSE);
gui_entry_erase(active_entry, 1, CUTBUFFER_UPDATE_NOOP);
}
static void key_delete_previous_word(void)
{
gui_entry_erase_word(active_entry, FALSE);
gui_entry_erase_word(active_entry, FALSE, CUTBUFFER_UPDATE_PREPEND);
}
static void key_delete_next_word(void)
{
gui_entry_erase_next_word(active_entry, FALSE);
gui_entry_erase_next_word(active_entry, FALSE, CUTBUFFER_UPDATE_APPEND);
}
static void key_delete_to_previous_space(void)
{
gui_entry_erase_word(active_entry, TRUE);
gui_entry_erase_word(active_entry, TRUE, CUTBUFFER_UPDATE_PREPEND);
}
static void key_delete_to_next_space(void)
{
gui_entry_erase_next_word(active_entry, TRUE);
gui_entry_erase_next_word(active_entry, TRUE, CUTBUFFER_UPDATE_APPEND);
}
static void key_append_next_kill(void)
{
active_entry->append_next_kill = TRUE;
}
static gboolean paste_timeout(gpointer data)
{
int split_lines;
paste_was_bracketed_mode = paste_bracketed_mode;
if (paste_line_count == 0) {
/* number of lines after splitting extra-long messages */
split_lines = paste_buffer->len / LINE_SPLIT_LIMIT;
/* Take into account the fact that a line may be split every LINE_SPLIT_LIMIT characters */
if (paste_line_count == 0 && split_lines <= paste_verify_line_count) {
int i;
for (i = 0; i < paste_buffer->len; i++) {
@ -647,8 +712,9 @@ static gboolean paste_timeout(gpointer data)
}
g_array_set_size(paste_buffer, 0);
} else if (paste_verify_line_count > 0 &&
paste_line_count >= paste_verify_line_count &&
active_win->active != NULL)
(paste_line_count >= paste_verify_line_count ||
split_lines > paste_verify_line_count) &&
active_win->active != NULL)
insert_paste_prompt();
else
paste_flush(TRUE);
@ -1170,6 +1236,8 @@ void gui_readline_init(void)
key_bind("erase_to_beg_of_line", "Erase everything before the cursor", NULL, NULL, (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_bind("erase_to_end_of_line", "Erase everything after the cursor", "^K", NULL, (SIGNAL_FUNC) key_erase_to_end_of_line);
key_bind("yank_from_cutbuffer", "\"Undelete\", paste the last deleted text", "^Y", NULL, (SIGNAL_FUNC) key_yank_from_cutbuffer);
key_bind("yank_next_cutbuffer", "Revert to the previous last deleted text", NULL, NULL, (SIGNAL_FUNC) key_yank_next_cutbuffer);
key_bind("append_next_kill", "Append next deletion", NULL, NULL, (SIGNAL_FUNC) key_append_next_kill);
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 the current word", NULL, NULL, (SIGNAL_FUNC) key_capitalize_word);
@ -1259,6 +1327,8 @@ void gui_readline_deinit(void)
key_unbind("erase_to_beg_of_line", (SIGNAL_FUNC) key_erase_to_beg_of_line);
key_unbind("erase_to_end_of_line", (SIGNAL_FUNC) key_erase_to_end_of_line);
key_unbind("yank_from_cutbuffer", (SIGNAL_FUNC) key_yank_from_cutbuffer);
key_unbind("yank_next_cutbuffer", (SIGNAL_FUNC) key_yank_next_cutbuffer);
key_unbind("append_next_kill", (SIGNAL_FUNC) key_append_next_kill);
key_unbind("transpose_characters", (SIGNAL_FUNC) key_transpose_characters);
key_unbind("transpose_words", (SIGNAL_FUNC) key_transpose_words);

View File

@ -74,10 +74,7 @@ void mainwindow_activity_deinit(void);
void mainwindows_layout_init(void);
void mainwindows_layout_deinit(void);
void term_dummy_init(void);
void term_dummy_deinit(void);
static int dirty, full_redraw, dummy;
static int dirty, full_redraw;
static GMainLoop *main_loop;
int quitting;
@ -122,7 +119,7 @@ void irssi_set_dirty(void)
static void dirty_check(void)
{
if (!dirty || dummy)
if (!dirty)
return;
term_resize_dirty();
@ -171,27 +168,22 @@ static void textui_finish_init(void)
{
quitting = FALSE;
if (dummy)
term_dummy_init();
else {
term_refresh_freeze();
textbuffer_init();
textbuffer_view_init();
textbuffer_commands_init();
gui_expandos_init();
gui_printtext_init();
gui_readline_init();
lastlog_init();
mainwindows_init();
mainwindow_activity_init();
mainwindows_layout_init();
gui_windows_init();
statusbar_init();
term_refresh_thaw();
term_refresh_freeze();
textbuffer_init();
textbuffer_view_init();
textbuffer_commands_init();
gui_expandos_init();
gui_printtext_init();
gui_readline_init();
lastlog_init();
mainwindows_init();
mainwindow_activity_init();
mainwindows_layout_init();
gui_windows_init();
statusbar_init();
term_refresh_thaw();
/* don't check settings with dummy mode */
settings_check();
}
settings_check();
module_register("core", "fe-text");
@ -233,25 +225,21 @@ static void textui_deinit(void)
dirty_check(); /* one last time to print any quit messages */
signal_remove("gui exit", (SIGNAL_FUNC) sig_exit);
if (dummy)
term_dummy_deinit();
else {
lastlog_deinit();
statusbar_deinit();
gui_printtext_deinit();
gui_readline_deinit();
gui_windows_deinit();
mainwindows_layout_deinit();
mainwindow_activity_deinit();
mainwindows_deinit();
gui_expandos_deinit();
textbuffer_commands_deinit();
textbuffer_view_deinit();
textbuffer_deinit();
lastlog_deinit();
statusbar_deinit();
gui_printtext_deinit();
gui_readline_deinit();
gui_windows_deinit();
mainwindows_layout_deinit();
mainwindow_activity_deinit();
mainwindows_deinit();
gui_expandos_deinit();
textbuffer_commands_deinit();
textbuffer_view_deinit();
textbuffer_deinit();
term_refresh_thaw();
term_deinit();
}
term_refresh_thaw();
term_deinit();
theme_unregister();
@ -276,7 +264,6 @@ int main(int argc, char **argv)
{
static int version = 0;
static GOptionEntry options[] = {
{ "dummy", 'd', 0, G_OPTION_ARG_NONE, &dummy, "Use the dummy terminal mode", NULL },
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Display irssi version", NULL },
{ NULL }
};
@ -295,7 +282,6 @@ int main(int argc, char **argv)
srand(time(NULL));
dummy = FALSE;
quitting = FALSE;
core_preinit(argv[0]);
@ -319,9 +305,8 @@ int main(int argc, char **argv)
loglev = g_log_set_always_fatal(G_LOG_FATAL_MASK | G_LOG_LEVEL_CRITICAL);
textui_init();
if (!dummy && !term_init()) {
fprintf(stderr, "Can't initialize screen handling, quitting.\n");
fprintf(stderr, "You can still use the dummy mode with -d parameter\n");
if (!term_init()) {
fprintf(stderr, "Can't initialize screen handling.\n");
return 1;
}
@ -332,9 +317,9 @@ int main(int argc, char **argv)
/* Does the same as g_main_run(main_loop), except we
can call our dirty-checker after each iteration */
while (!quitting) {
if (!dummy) term_refresh_freeze();
term_refresh_freeze();
g_main_iteration(TRUE);
if (!dummy) term_refresh_thaw();
term_refresh_thaw();
if (reload_config) {
/* SIGHUP received, do /RELOAD */

View File

@ -39,21 +39,10 @@
Returns -1 if unknown option was given. */
int cmd_options_get_level(const char *cmd, GHashTable *optlist)
{
GSList *list, *tmp, *next;
GList *list;
int level, retlevel;
/* get all the options, then remove the known ones. there should
be only one left - the server tag. */
list = hashtable_get_keys(optlist);
if (cmd != NULL) {
for (tmp = list; tmp != NULL; tmp = next) {
char *option = tmp->data;
next = tmp->next;
if (command_have_option(cmd, option))
list = g_slist_remove(list, option);
}
}
list = optlist_remove_known(cmd, optlist);
retlevel = 0;
while (list != NULL) {
@ -68,7 +57,7 @@ int cmd_options_get_level(const char *cmd, GHashTable *optlist)
}
retlevel |= level;
list = g_slist_remove(list, list->data);
list = g_list_remove(list, list->data);
}
return retlevel;

View File

@ -289,6 +289,10 @@ static void sig_statusbar_more_updated(void)
{
int visible;
/* no active window, for example during /window hide */
if (active_win == NULL)
return;
visible = g_slist_find(more_visible, WINDOW_MAIN(active_win)) != NULL;
if (WINDOW_GUI(active_win)->view->more_text != visible)
statusbar_items_redraw("more");

View File

@ -671,8 +671,8 @@ void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
WI_ITEM_REC *wiitem;
char *tmpstr, *tmpstr2;
theme_rm_col reset;
strcpy(reset.m, "n");
int len;
strcpy(reset.m, "n");
if (str == NULL)
str = statusbar_item_get_value(item);

View File

@ -1,415 +0,0 @@
/*
term-curses.c : irssi
Copyright (C) 1999-2001 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "signals.h"
#include "settings.h"
#include "term.h"
#include "mainwindows.h"
#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
# include <ncurses.h>
#else
# include <curses.h>
#endif
#include <termios.h>
#include <signal.h>
#ifndef COLOR_PAIRS
# define COLOR_PAIRS 64
#endif
#if defined (TIOCGWINSZ) && defined (HAVE_CURSES_RESIZETERM)
# define USE_RESIZE_TERM
#endif
#ifndef _POSIX_VDISABLE
# define _POSIX_VDISABLE 0
#endif
struct _TERM_WINDOW {
int x, y;
int width, height;
WINDOW *win;
};
TERM_WINDOW *root_window;
static int curs_x, curs_y;
static int freeze_refresh;
static struct termios old_tio;
static int init_curses(void)
{
char ansi_tab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
int num;
struct termios tio;
if (!initscr())
return FALSE;
cbreak(); noecho(); idlok(stdscr, 1);
#ifdef HAVE_CURSES_IDCOK
/*idcok(stdscr, 1); - disabled currently, causes redrawing problems with NetBSD */
#endif
intrflush(stdscr, FALSE); nodelay(stdscr, TRUE);
/* Disable INTR, QUIT, VDSUSP and SUSP keys */
if (tcgetattr(0, &old_tio) == 0) {
memcpy(&tio, &old_tio, sizeof(tio));
tio.c_cc[VINTR] = _POSIX_VDISABLE;
tio.c_cc[VQUIT] = _POSIX_VDISABLE;
#ifdef VDSUSP
tio.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif
#ifdef VSUSP
tio.c_cc[VSUSP] = _POSIX_VDISABLE;
#endif
tcsetattr(0, TCSADRAIN, &tio);
}
if (has_colors())
start_color();
else if (term_use_colors)
term_use_colors = FALSE;
#ifdef HAVE_NCURSES_USE_DEFAULT_COLORS
/* this lets us to use the "default" background color for colors <= 7 so
background pixmaps etc. show up right */
use_default_colors();
for (num = 1; num < COLOR_PAIRS; num++)
init_pair(num, ansi_tab[num & 7], num <= 7 ? -1 : ansi_tab[num >> 3]);
init_pair(63, 0, -1); /* hm.. not THAT good idea, but probably more
people want dark grey than white on white.. */
#else
for (num = 1; num < COLOR_PAIRS; num++)
init_pair(num, ansi_tab[num & 7], ansi_tab[num >> 3]);
init_pair(63, 0, 0);
#endif
clear();
return TRUE;
}
static int term_init_int(void)
{
int ret;
ret = init_curses();
if (!ret) return 0;
curs_x = curs_y = 0;
freeze_refresh = 0;
root_window = g_new0(TERM_WINDOW, 1);
root_window->win = stdscr;
term_width = COLS;
term_height = LINES;
return ret;
}
static void term_deinit_int(void)
{
tcsetattr(0, TCSADRAIN, &old_tio);
endwin();
g_free_and_null(root_window);
}
int term_init(void)
{
if (!term_init_int())
return FALSE;
settings_add_int("lookandfeel", "default_color", 7);
term_common_init();
return TRUE;
}
void term_deinit(void)
{
term_common_deinit();
term_deinit_int();
}
/* Resize terminal - if width or height is negative,
the new size is unknown and should be figured out somehow */
void term_resize(int width, int height)
{
#ifdef HAVE_CURSES_RESIZETERM
if (width < 0 || height < 0) {
#endif
term_deinit_int();
term_init_int();
#ifdef HAVE_CURSES_RESIZETERM
} else if (term_width != width || term_height != height) {
term_width = width;
term_height = height;
resizeterm(term_height, term_width);
}
#endif
}
void term_resize_final(int width, int height)
{
#ifdef HAVE_CURSES_RESIZETERM
if (width < 0 || height < 0)
mainwindows_recreate();
#else
mainwindows_recreate();
#endif
}
/* Returns TRUE if terminal has colors */
int term_has_colors(void)
{
return has_colors();
}
/* Force the colors on any way you can */
void term_force_colors(int set)
{
/* don't do anything with curses */
}
/* Clear screen */
void term_clear(void)
{
term_set_color(root_window, 0);
clear();
}
/* Beep */
void term_beep(void)
{
beep();
}
/* Create a new window in terminal */
TERM_WINDOW *term_window_create(int x, int y, int width, int height)
{
TERM_WINDOW *window;
window = g_new0(TERM_WINDOW, 1);
window->x = x; window->y = y;
window->width = width; window->height = height;
window->win = newwin(height, width, y, x);
if (window->win == NULL)
g_error("newwin() failed: %d,%d %d,%d", x, y, width, height);
idlok(window->win, 1);
return window;
}
/* Destroy a terminal window */
void term_window_destroy(TERM_WINDOW *window)
{
delwin(window->win);
g_free(window);
}
/* Move/resize a window */
void term_window_move(TERM_WINDOW *window, int x, int y,
int width, int height)
{
/* some checks to make sure the window is visible in screen,
otherwise curses could get nasty and not show our window anymore. */
if (width < 1) width = 1;
if (height < 1) height = 1;
if (x+width > term_width) x = term_width-width;
if (y+height > term_height) y = term_height-height;
#ifdef HAVE_CURSES_WRESIZE
if (window->width != width || window->height != height)
wresize(window->win, height, width);
if (window->x != x || window->y != y)
mvwin(window->win, y, x);
#else
if (window->width != width || window->height != height ||
window->x != x || window->y != y) {
delwin(window->win);
window->win = newwin(height, width, y, x);
idlok(window->win, 1);
}
#endif
window->x = x; window->y = y;
window->width = width; window->height = height;
}
/* Clear window */
void term_window_clear(TERM_WINDOW *window)
{
werase(window->win);
}
/* Scroll window up/down */
void term_window_scroll(TERM_WINDOW *window, int count)
{
scrollok(window->win, TRUE);
wscrl(window->win, count);
scrollok(window->win, FALSE);
}
static int get_attr(int color)
{
int attr;
if ((color & FG_MASK) >> 4)
color = (color & ~FG_MASK) | term_color256map[color & FG_MASK];
if ((color & BG_MASK) >> (BG_SHIFT + 4))
color = (color & ~BG_MASK) | (term_color256map[(color & BG_MASK) >> BG_SHIFT] << BG_SHIFT);
if (!term_use_colors)
attr = (color & (0x7 << BG_SHIFT)) ? A_REVERSE : 0;
else if ((color & ((0xf << BG_SHIFT) | 0xf)) == 8 || (color & (FG_MASK | BG_MASK | ATTR_RESETFG)) == 0)
attr = COLOR_PAIR(63);
else if ((color & ((0x7 << BG_SHIFT) | 0x7)) == 0)
attr = A_NORMAL;
else {
if (color & ATTR_RESETFG) {
color &= ~FG_MASK;
color |= settings_get_int("default_color");
}
attr = COLOR_PAIR((color&0x7) | ((color&(0x7<<BG_SHIFT))>>BG_SHIFT<<3));
}
if ((color & 0x8) || (color & ATTR_BOLD)) attr |= A_BOLD;
if (color & ATTR_BLINK) attr |= A_BLINK;
if (color & ATTR_UNDERLINE) attr |= A_UNDERLINE;
if (color & ATTR_REVERSE) attr |= A_REVERSE;
#ifdef A_ITALIC
if (color & ATTR_ITALIC) attr |= A_ITALIC;
#endif
return attr;
}
/* Change active color */
void term_set_color(TERM_WINDOW *window, int col)
{
wattrset(window->win, get_attr(col));
wbkgdset(window->win, ' ' | get_attr(col));
}
void term_move(TERM_WINDOW *window, int x, int y)
{
wmove(window->win, y, x);
}
void term_addch(TERM_WINDOW *window, char chr)
{
waddch(window->win, chr);
}
void term_add_unichar(TERM_WINDOW *window, unichar chr)
{
#ifdef WIDEC_CURSES
cchar_t wch;
wchar_t temp[2];
temp[0] = chr;
temp[1] = 0;
if (setcchar(&wch, temp, A_NORMAL, 0, NULL) == OK)
wadd_wch(window->win, &wch);
else
#endif
waddch(window->win, chr);
}
void term_addstr(TERM_WINDOW *window, const char *str)
{
waddstr(window->win, (const char *) str);
}
void term_clrtoeol(TERM_WINDOW *window)
{
wclrtoeol(window->win);
}
void term_move_cursor(int x, int y)
{
curs_x = x;
curs_y = y;
}
void term_refresh_freeze(void)
{
freeze_refresh++;
}
void term_refresh_thaw(void)
{
if (freeze_refresh > 0) {
freeze_refresh--;
if (freeze_refresh == 0) term_refresh(NULL);
}
}
void term_refresh(TERM_WINDOW *window)
{
if (window != NULL)
wnoutrefresh(window->win);
if (freeze_refresh == 0) {
move(curs_y, curs_x);
wnoutrefresh(stdscr);
doupdate();
}
}
void term_stop(void)
{
term_deinit_int();
kill(getpid(), SIGTSTP);
term_init_int();
irssi_redraw();
}
void term_set_input_type(int type)
{
}
void term_gets(GArray *buffer, int *line_count)
{
#ifdef WIDEC_CURSES
wint_t key;
#else
int key;
#endif
for (;;) {
#ifdef WIDEC_CURSES
if (get_wch(&key) == ERR)
#else
if ((key = getch()) == ERR)
#endif
break;
#ifdef KEY_RESIZE
if (key == KEY_RESIZE)
continue;
#endif
g_array_append_val(buffer, key);
if (key == '\r' || key == '\n')
(*line_count)++;
}
}

View File

@ -1,106 +0,0 @@
/*
term-dummy.c : irssi
Copyright (C) 2001 Timo Sirainen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "signals.h"
#include "fe-windows.h"
static int newline;
static GIOChannel *stdin_channel;
static int readtag;
static GString *input;
static void sig_gui_printtext(WINDOW_REC *window, void *fgcolor,
void *bgcolor, void *pflags,
char *str, void *level)
{
if (newline) {
newline = FALSE;
printf("\r");
}
printf("%s", str);
}
static void sig_gui_printtext_finished(WINDOW_REC *window)
{
printf("\n");
newline = TRUE;
}
static void sig_window_created(WINDOW_REC *window)
{
window->width = 80;
window->height = 25;
}
static void readline(void)
{
unsigned char buffer[128];
char *p;
int ret, i;
ret = read(0, buffer, sizeof(buffer));
if (ret == 0 || (ret == -1 && errno != EINTR)) {
/* lost terminal */
signal_emit("command quit", 1, "Lost terminal");
return;
}
for (i = 0; i < ret; i++)
g_string_append_c(input, buffer[i]);
p = strchr(input->str, '\n');
if (p != NULL) {
*p = '\0';
signal_emit("send command", 3, input->str,
active_win->active_server, active_win->active);
*p = '\n';
g_string_erase(input, 0, (int) (p-input->str)+1);
}
}
void term_dummy_init(void)
{
newline = TRUE;
input = g_string_new(NULL);
signal_add("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
signal_add("window created", (SIGNAL_FUNC) sig_window_created);
stdin_channel = g_io_channel_unix_new(0);
readtag = g_input_add_full(stdin_channel,
G_PRIORITY_HIGH, G_INPUT_READ,
(GInputFunction) readline, NULL);
g_io_channel_unref(stdin_channel);
}
void term_dummy_deinit(void)
{
signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_printtext);
signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
signal_remove("window created", (SIGNAL_FUNC) sig_window_created);
g_source_remove(readtag);
g_string_free(input, TRUE);
}

View File

@ -611,8 +611,6 @@ void term_stop(void)
{
terminfo_stop(current_term);
kill(getpid(), SIGTSTP);
terminfo_cont(current_term);
irssi_redraw();
}
static int input_utf8(const unsigned char *buffer, int size, unichar *result)

View File

@ -27,7 +27,7 @@ typedef struct _TERM_WINDOW TERM_WINDOW;
#define TERM_TYPE_UTF8 1
#define TERM_TYPE_BIG5 2
typedef guint32 unichar;
#include "utf8.h"
extern TERM_WINDOW *root_window;
extern int term_width, term_height;

View File

@ -17,7 +17,6 @@ inline static int term_putchar(int c)
char *tparm();
int tputs();
#ifdef HAVE_TERMINFO
int setupterm();
char *tigetstr();
int tigetnum();
@ -25,15 +24,6 @@ int tigetflag();
#define term_getstr(x, buffer) tigetstr(x.ti_name)
#define term_getnum(x) tigetnum(x.ti_name);
#define term_getflag(x) tigetflag(x.ti_name);
#else
int tgetent();
char *tgetstr();
int tgetnum();
int tgetflag();
#define term_getstr(x, buffer) tgetstr(x.tc_name, &buffer)
#define term_getnum(x) tgetnum(x.tc_name)
#define term_getflag(x) tgetflag(x.tc_name)
#endif
#define CAP_TYPE_FLAG 0
#define CAP_TYPE_INT 1
@ -415,9 +405,6 @@ static void term_fill_capabilities(TERM_REC *term)
char *sval;
void *ptr;
#ifndef HAVE_TERMINFO
char *tptr = term->buffer2;
#endif
for (i = 0; i < sizeof(tcaps)/sizeof(tcaps[0]); i++) {
ptr = G_STRUCT_MEMBER_P(term, tcaps[i].offset);
@ -583,9 +570,7 @@ void terminfo_stop(TERM_REC *term)
static int term_setup(TERM_REC *term)
{
GString *str;
#ifdef HAVE_TERMINFO
int err;
#endif
char *term_env;
term_env = getenv("TERM");
@ -594,18 +579,10 @@ static int term_setup(TERM_REC *term)
return 0;
}
#ifdef HAVE_TERMINFO
if (setupterm(term_env, 1, &err) != 0) {
fprintf(stderr, "setupterm() failed for TERM=%s: %d\n", term_env, err);
return 0;
}
#else
if (tgetent(term->buffer1, term_env) < 1)
{
fprintf(stderr, "Termcap not found for TERM=%s\n", term_env);
return 0;
}
#endif
term_fill_capabilities(term);

View File

@ -1,740 +0,0 @@
/*
* tparm.c
*
* By Ross Ridge
* Public Domain
* 92/02/01 07:30:36
*
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifndef MAX_PUSHED
#define MAX_PUSHED 32
#endif
#define ARG 1
#define NUM 2
#define INTEGER 1
#define STRING 2
#define MAX_LINE 640
typedef void* anyptr;
typedef struct stack_str {
int type;
int argnum;
int value;
} stack;
static stack S[MAX_PUSHED];
static stack vars['z'-'a'+1];
static int pos = 0;
static struct arg_str {
int type;
int integer;
char *string;
} arg_list[10];
static int argcnt;
static va_list tparm_args;
static int pusharg(int arg)
{
if (pos == MAX_PUSHED)
return 1;
S[pos].type = ARG;
S[pos++].argnum = arg;
return 0;
}
static int pushnum(int num)
{
if (pos == MAX_PUSHED)
return 1;
S[pos].type = NUM;
S[pos++].value = num;
return 0;
}
/* VARARGS2 */
static int getarg(int argnum, int type, anyptr p)
{
while (argcnt < argnum) {
arg_list[argcnt].type = INTEGER;
arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
}
if (argcnt > argnum) {
if (arg_list[argnum].type != type)
return 1;
else if (type == STRING)
*(char **)p = arg_list[argnum].string;
else
*(int *)p = arg_list[argnum].integer;
} else {
arg_list[argcnt].type = type;
if (type == STRING)
*(char **)p = arg_list[argcnt++].string
= (char *) va_arg(tparm_args, char *);
else
*(int *)p = arg_list[argcnt++].integer = (int) va_arg(tparm_args, int);
}
return 0;
}
static int popstring(char **str)
{
if (pos-- == 0)
return 1;
if (S[pos].type != ARG)
return 1;
return(getarg(S[pos].argnum, STRING, (anyptr) str));
}
static int popnum(int *num)
{
if (pos-- == 0)
return 1;
switch (S[pos].type) {
case ARG:
return (getarg(S[pos].argnum, INTEGER, (anyptr) num));
case NUM:
*num = S[pos].value;
return 0;
}
return 1;
}
static int cvtchar(const char *sp, char *c)
{
switch(*sp) {
case '\\':
switch(*++sp) {
case '\'':
case '$':
case '\\':
case '%':
*c = *sp;
return 2;
case '\0':
*c = '\\';
return 1;
case '0':
if (sp[1] == '0' && sp[2] == '0') {
*c = '\0';
return 4;
}
*c = '\200'; /* '\0' ???? */
return 2;
default:
*c = *sp;
return 2;
}
default:
*c = *sp;
return 1;
}
}
static int termcap;
/* sigh... this has got to be the ugliest code I've ever written.
Trying to handle everything has its cost, I guess.
It actually isn't to hard to figure out if a given % code is supposed
to be interpeted with its termcap or terminfo meaning since almost
all terminfo codes are invalid unless something has been pushed on
the stack and termcap strings will never push things on the stack
(%p isn't used by termcap). So where we have a choice we make the
decision by whether or not somthing has been pushed on the stack.
The static variable termcap keeps track of this; it starts out set
to 1 and is incremented as each argument processed by a termcap % code,
however if something is pushed on the stack it's set to 0 and the
rest of the % codes are interpeted as terminfo % codes. Another way
of putting it is that if termcap equals one we haven't decided either
way yet, if it equals zero we're looking for terminfo codes, and if
its greater than 1 we're looking for termcap codes.
Terminfo % codes:
%% output a '%'
%[[:][-+# ][width][.precision]][doxXs]
output pop according to the printf format
%c output pop as a char
%'c' push character constant c.
%{n} push decimal constant n.
%p[1-9] push parameter [1-9]
%g[a-z] push variable [a-z]
%P[a-z] put pop in variable [a-z]
%l push the length of pop (a string)
%+ add pop to pop and push the result
%- subtract pop from pop and push the result
%* multiply pop and pop and push the result
%& bitwise and pop and pop and push the result
%| bitwise or pop and pop and push the result
%^ bitwise xor pop and pop and push the result
%~ push the bitwise not of pop
%= compare if pop and pop are equal and push the result
%> compare if pop is less than pop and push the result
%< compare if pop is greater than pop and push the result
%A logical and pop and pop and push the result
%O logical or pop and pop and push the result
%! push the logical not of pop
%? condition %t if_true [%e if_false] %;
if condition evaulates as true then evaluate if_true,
else evaluate if_false. elseif's can be done:
%? cond %t true [%e cond2 %t true2] ... [%e condN %t trueN] [%e false] %;
%i add one to parameters 1 and 2. (ANSI)
Termcap Codes:
%% output a %
%. output parameter as a character
%d output parameter as a decimal number
%2 output parameter in printf format %02d
%3 output parameter in printf format %03d
%+x add the character x to parameter and output it as a character
(UW) %-x subtract parameter FROM the character x and output it as a char
(UW) %ax add the character x to parameter
(GNU) %a[+*-/=][cp]x
GNU arithmetic.
(UW) %sx subtract parameter FROM the character x
%>xy if parameter > character x then add character y to parameter
%B convert to BCD (parameter = (parameter/10)*16 + parameter%16)
%D Delta Data encode (parameter = parameter - 2*(parameter%16))
%i increment the first two parameters by one
%n xor the first two parameters by 0140
(GNU) %m xor the first two parameters by 0177
%r swap the first two parameters
(GNU) %b backup to previous parameter
(GNU) %f skip this parameter
Note the two definitions of %a, the GNU definition is used if the characters
after the 'a' are valid, otherwise the UW definition is used.
(GNU) used by GNU Emacs termcap libraries
(UW) used by the University of Waterloo (MFCF) termcap libraries
*/
char *tparm(const char *str, ...) {
static char OOPS[] = "OOPS";
static char buf[MAX_LINE];
register const char *sp;
register char *dp;
register char *fmt;
char conv_char;
char scan_for;
int scan_depth = 0, if_depth;
static int i, j;
static char *s, c;
char fmt_buf[MAX_LINE];
char sbuf[MAX_LINE];
va_start(tparm_args, str);
sp = str;
dp = buf;
scan_for = 0;
if_depth = 0;
argcnt = 0;
pos = 0;
termcap = 1;
while (*sp != '\0') {
switch(*sp) {
case '\\':
if (scan_for) {
if (*++sp != '\0')
sp++;
break;
}
*dp++ = *sp++;
if (*sp != '\0')
*dp++ = *sp++;
break;
case '%':
sp++;
if (scan_for) {
if (*sp == scan_for && if_depth == scan_depth) {
if (scan_for == ';')
if_depth--;
scan_for = 0;
} else if (*sp == '?')
if_depth++;
else if (*sp == ';') {
if (if_depth == 0)
return OOPS;
else
if_depth--;
}
sp++;
break;
}
fmt = NULL;
switch(*sp) {
case '%':
*dp++ = *sp++;
break;
case '+':
if (!termcap) {
if (popnum(&j) || popnum(&i))
return OOPS;
i += j;
if (pushnum(i))
return OOPS;
sp++;
break;
}
;/* FALLTHROUGH */
case 'C':
if (*sp == 'C') {
if (getarg(termcap - 1, INTEGER, &i))
return OOPS;
if (i >= 96) {
i /= 96;
if (i == '$')
*dp++ = '\\';
*dp++ = i;
}
}
fmt = "%c";
/* FALLTHROUGH */
case 'a':
if (!termcap)
return OOPS;
if (getarg(termcap - 1, INTEGER, (anyptr) &i))
return OOPS;
if (*++sp == '\0')
return OOPS;
if ((sp[1] == 'p' || sp[1] == 'c')
&& sp[2] != '\0' && fmt == NULL) {
/* GNU aritmitic parameter, what they
really need is terminfo. */
int val, lc;
if (sp[1] == 'p'
&& getarg(termcap - 1 + sp[2] - '@',
INTEGER, (anyptr) &val))
return OOPS;
if (sp[1] == 'c') {
lc = cvtchar(sp + 2, &c) + 2;
/* Mask out 8th bit so \200 can be
used for \0 as per GNU doc's */
val = c & 0177;
} else
lc = 2;
switch(sp[0]) {
case '=':
break;
case '+':
val = i + val;
break;
case '-':
val = i - val;
break;
case '*':
val = i * val;
break;
case '/':
val = i / val;
break;
default:
/* Not really GNU's %a after all... */
lc = cvtchar(sp, &c);
val = c + i;
break;
}
arg_list[termcap - 1].integer = val;
sp += lc;
break;
}
sp += cvtchar(sp, &c);
arg_list[termcap - 1].integer = c + i;
if (fmt == NULL)
break;
sp--;
/* FALLTHROUGH */
case '-':
if (!termcap) {
if (popnum(&j) || popnum(&i))
return OOPS;
i -= j;
if (pushnum(i))
return OOPS;
sp++;
break;
}
fmt = "%c";
/* FALLTHROUGH */
case 's':
if (termcap && (fmt == NULL || *sp == '-')) {
if (getarg(termcap - 1, INTEGER, &i))
return OOPS;
if (*++sp == '\0')
return OOPS;
sp += cvtchar(sp, &c);
arg_list[termcap - 1].integer = c - i;
if (fmt == NULL)
break;
sp--;
}
if (!termcap)
return OOPS;
;/* FALLTHROUGH */
case '.':
if (termcap && fmt == NULL)
fmt = "%c";
;/* FALLTHROUGH */
case 'd':
if (termcap && fmt == NULL)
fmt = "%d";
;/* FALLTHROUGH */
case '2':
if (termcap && fmt == NULL)
fmt = "%02d";
;/* FALLTHROUGH */
case '3':
if (termcap && fmt == NULL)
fmt = "%03d";
;/* FALLTHROUGH */
case ':': case ' ': case '#': case 'u':
case 'x': case 'X': case 'o': case 'c':
case '0': case '1': case '4': case '5':
case '6': case '7': case '8': case '9':
if (fmt == NULL) {
if (termcap)
return OOPS;
if (*sp == ':')
sp++;
fmt = fmt_buf;
*fmt++ = '%';
while(*sp != 's' && *sp != 'x' && *sp != 'X' && *sp != 'd' && *sp != 'o' && *sp != 'c' && *sp != 'u') {
if (*sp == '\0')
return OOPS;
*fmt++ = *sp++;
}
*fmt++ = *sp;
*fmt = '\0';
fmt = fmt_buf;
}
conv_char = fmt[strlen(fmt) - 1];
if (conv_char == 's') {
if (popstring(&s))
return OOPS;
sprintf(sbuf, fmt, s);
} else {
if (termcap) {
if (getarg(termcap++ - 1,
INTEGER, &i))
return OOPS;
} else
if (popnum(&i))
return OOPS;
if (i == 0 && conv_char == 'c')
*sbuf = 0;
else
sprintf(sbuf, fmt, i);
}
sp++;
fmt = sbuf;
while(*fmt != '\0') {
if (*fmt == '$')
*dp++ = '\\';
*dp++ = *fmt++;
}
break;
case 'r':
if (!termcap || getarg(1, INTEGER, &i))
return OOPS;
arg_list[1].integer = arg_list[0].integer;
arg_list[0].integer = i;
sp++;
break;
case 'i':
if (getarg(1, INTEGER, &i)
|| arg_list[0].type != INTEGER)
return OOPS;
arg_list[1].integer++;
arg_list[0].integer++;
sp++;
break;
case 'n':
if (!termcap || getarg(1, INTEGER, &i))
return OOPS;
arg_list[0].integer ^= 0140;
arg_list[1].integer ^= 0140;
sp++;
break;
case '>':
if (!termcap) {
if (popnum(&j) || popnum(&i))
return OOPS;
i = (i > j);
if (pushnum(i))
return OOPS;
sp++;
break;
}
if (getarg(termcap-1, INTEGER, &i))
return OOPS;
sp += cvtchar(sp, &c);
if (i > c) {
sp += cvtchar(sp, &c);
arg_list[termcap-1].integer += c;
} else
sp += cvtchar(sp, &c);
sp++;
break;
case 'B':
if (!termcap || getarg(termcap-1, INTEGER, &i))
return OOPS;
arg_list[termcap-1].integer = 16*(i/10)+i%10;
sp++;
break;
case 'D':
if (!termcap || getarg(termcap-1, INTEGER, &i))
return OOPS;
arg_list[termcap-1].integer = i - 2 * (i % 16);
sp++;
break;
case 'p':
if (termcap > 1)
return OOPS;
if (*++sp == '\0')
return OOPS;
if (*sp == '0')
i = 9;
else
i = *sp - '1';
if (i < 0 || i > 9)
return OOPS;
if (pusharg(i))
return OOPS;
termcap = 0;
sp++;
break;
case 'P':
if (termcap || *++sp == '\0')
return OOPS;
i = *sp++ - 'a';
if (i < 0 || i > 25)
return OOPS;
if (pos-- == 0)
return OOPS;
switch(vars[i].type = S[pos].type) {
case ARG:
vars[i].argnum = S[pos].argnum;
break;
case NUM:
vars[i].value = S[pos].value;
break;
}
break;
case 'g':
if (termcap || *++sp == '\0')
return OOPS;
i = *sp++ - 'a';
if (i < 0 || i > 25)
return OOPS;
switch(vars[i].type) {
case ARG:
if (pusharg(vars[i].argnum))
return OOPS;
break;
case NUM:
if (pushnum(vars[i].value))
return OOPS;
break;
}
break;
case '\'':
if (termcap > 1)
return OOPS;
if (*++sp == '\0')
return OOPS;
sp += cvtchar(sp, &c);
if (pushnum(c) || *sp++ != '\'')
return OOPS;
termcap = 0;
break;
case '{':
if (termcap > 1)
return OOPS;
i = 0;
sp++;
while(isdigit((int) (unsigned char) *sp))
i = 10 * i + *sp++ - '0';
if (*sp++ != '}' || pushnum(i))
return OOPS;
termcap = 0;
break;
case 'l':
if (termcap || popstring(&s))
return OOPS;
i = strlen(s);
if (pushnum(i))
return OOPS;
sp++;
break;
case '*':
if (termcap || popnum(&j) || popnum(&i))
return OOPS;
i *= j;
if (pushnum(i))
return OOPS;
sp++;
break;
case '/':
if (termcap || popnum(&j) || popnum(&i))
return OOPS;
i /= j;
if (pushnum(i))
return OOPS;
sp++;
break;
case 'm':
if (termcap) {
if (getarg(1, INTEGER, &i))
return OOPS;
arg_list[0].integer ^= 0177;
arg_list[1].integer ^= 0177;
sp++;
break;
}
if (popnum(&j) || popnum(&i))
return OOPS;
i %= j;
if (pushnum(i))
return OOPS;
sp++;
break;
case '&':
if (popnum(&j) || popnum(&i))
return OOPS;
i &= j;
if (pushnum(i))
return OOPS;
sp++;
break;
case '|':
if (popnum(&j) || popnum(&i))
return OOPS;
i |= j;
if (pushnum(i))
return OOPS;
sp++;
break;
case '^':
if (popnum(&j) || popnum(&i))
return OOPS;
i ^= j;
if (pushnum(i))
return OOPS;
sp++;
break;
case '=':
if (popnum(&j) || popnum(&i))
return OOPS;
i = (i == j);
if (pushnum(i))
return OOPS;
sp++;
break;
case '<':
if (popnum(&j) || popnum(&i))
return OOPS;
i = (i < j);
if (pushnum(i))
return OOPS;
sp++;
break;
case 'A':
if (popnum(&j) || popnum(&i))
return OOPS;
i = (i && j);
if (pushnum(i))
return OOPS;
sp++;
break;
case 'O':
if (popnum(&j) || popnum(&i))
return OOPS;
i = (i || j);
if (pushnum(i))
return OOPS;
sp++;
break;
case '!':
if (popnum(&i))
return OOPS;
i = !i;
if (pushnum(i))
return OOPS;
sp++;
break;
case '~':
if (popnum(&i))
return OOPS;
i = ~i;
if (pushnum(i))
return OOPS;
sp++;
break;
case '?':
if (termcap > 1)
return OOPS;
termcap = 0;
if_depth++;
sp++;
break;
case 't':
if (popnum(&i) || if_depth == 0)
return OOPS;
if (!i) {
scan_for = 'e';
scan_depth = if_depth;
}
sp++;
break;
case 'e':
if (if_depth == 0)
return OOPS;
scan_for = ';';
scan_depth = if_depth;
sp++;
break;
case ';':
if (if_depth-- == 0)
return OOPS;
sp++;
break;
case 'b':
if (--termcap < 1)
return OOPS;
sp++;
break;
case 'f':
if (!termcap++)
return OOPS;
sp++;
break;
}
break;
default:
if (scan_for)
sp++;
else
*dp++ = *sp++;
break;
}
}
va_end(tparm_args);
*dp = '\0';
return buf;
}

View File

@ -24,10 +24,12 @@
void irc_channels_setup_init(void)
{
signal_add("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
signal_add("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
signal_add("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}
void irc_channels_setup_deinit(void)
{
signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_autocommands);
signal_remove("channel wholist", (SIGNAL_FUNC) channel_send_botcommands);
signal_remove("channel joined", (SIGNAL_FUNC) channel_send_autocommands);
}

View File

@ -48,7 +48,7 @@ NICK_REC *irc_nicklist_insert(IRC_CHANNEL_REC *channel, const char *nick,
rec->send_massjoin = send_massjoin;
if (prefixes != NULL) {
strocpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
g_strlcpy(rec->prefixes, prefixes, sizeof(rec->prefixes));
}
nicklist_insert(CHANNEL(channel), rec);

View File

@ -214,7 +214,6 @@ static void server_init(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
char *address, *ptr, *username, *cmd;
GTimeVal now;
g_return_if_fail(server != NULL);
@ -289,9 +288,8 @@ static void server_init(IRC_SERVER_REC *server)
/* prevent the queue from sending too early, we have a max cut off of 120 secs */
/* this will reset to 1 sec after we get the 001 event */
g_get_current_time(&now);
memcpy(&((IRC_SERVER_REC *)server)->wait_cmd, &now, sizeof(GTimeVal));
((IRC_SERVER_REC *)server)->wait_cmd.tv_sec += 120;
g_get_current_time(&server->wait_cmd);
g_time_val_add(&server->wait_cmd, 120 * G_USEC_PER_SEC);
}
SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
@ -537,7 +535,7 @@ void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len)
server->wait_cmd.tv_sec = 0;
else {
memcpy(&server->wait_cmd, &server->last_cmd, sizeof(GTimeVal));
server->wait_cmd.tv_sec += 2 + len/100;
g_time_val_add(&server->wait_cmd, (2 + len/100) * G_USEC_PER_SEC);
}
}
@ -625,39 +623,25 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
GString *chans, *keys;
char *ret;
int use_keys;
char *rejoin_channels_mode;
int rejoin_channels_mode;
g_return_val_if_fail(server != NULL, FALSE);
rejoin_channels_mode = g_strdup(settings_get_str("rejoin_channels_on_reconnect"));
rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect");
if (rejoin_channels_mode == NULL ||
(g_ascii_strcasecmp(rejoin_channels_mode, "on") != 0 &&
g_ascii_strcasecmp(rejoin_channels_mode, "off") != 0 &&
g_ascii_strcasecmp(rejoin_channels_mode, "auto") != 0)) {
g_warning("Invalid value for 'rejoin_channels_on_reconnect', valid values are 'on', 'off', 'auto', using 'on' as default value.");
g_free(rejoin_channels_mode);
rejoin_channels_mode = g_strdup("on");
}
/* do we want to rejoin channels in the first place? */
if(rejoin_channels_mode == 0)
return g_strdup("");
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
/* do we want to rejoin channels in the first place? */
if(g_ascii_strcasecmp(rejoin_channels_mode, "off") == 0) {
g_string_free(chans, TRUE);
g_string_free(keys, TRUE);
g_free(rejoin_channels_mode);
return g_strdup("");
}
/* get currently joined channels */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
if ((setup != NULL && setup->autojoin && g_ascii_strcasecmp(rejoin_channels_mode, "auto") == 0) ||
g_ascii_strcasecmp(rejoin_channels_mode, "on") == 0) {
if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
g_string_append_printf(chans, "%s,", channel->name);
g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
if (channel->key != NULL)
@ -670,8 +654,7 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
REJOIN_REC *rec = tmp->data;
CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
if ((setup != NULL && setup->autojoin && g_ascii_strcasecmp(rejoin_channels_mode, "auto") == 0) ||
g_ascii_strcasecmp(rejoin_channels_mode, "on") == 0) {
if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
g_string_append_printf(chans, "%s,", rec->channel);
g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
rec->key);
@ -689,7 +672,6 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
ret = chans->str;
g_string_free(chans, FALSE);
g_string_free(keys, TRUE);
g_free(rejoin_channels_mode);
return ret;
}
@ -697,7 +679,6 @@ char *irc_server_get_channels(IRC_SERVER_REC *server)
static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
{
char *params, *nick;
GTimeVal now;
g_return_if_fail(server != NULL);
@ -720,8 +701,7 @@ static void event_connected(IRC_SERVER_REC *server, const char *data, const char
server->real_connect_time = time(NULL);
/* let the queue send now that we are identified */
g_get_current_time(&now);
memcpy(&server->wait_cmd, &now, sizeof(GTimeVal));
g_get_current_time(&server->wait_cmd);
if (server->connrec->usermode != NULL) {
/* Send the user mode, before the autosendcmd.
@ -1035,7 +1015,7 @@ void irc_server_init_isupport(IRC_SERVER_REC *server)
void irc_servers_init(void)
{
settings_add_str("servers", "rejoin_channels_on_reconnect", "on");
settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
settings_add_str("misc", "split_line_start", "");
settings_add_str("misc", "split_line_end", "");

View File

@ -619,7 +619,7 @@ static void ctcp_msg_dcc_chat(IRC_SERVER_REC *server, const char *data,
/* CHAT <unused> <address> <port> */
/* CHAT <unused> <address> 0 <id> (DCC CHAT passive protocol) */
params = g_strsplit(data, " ", -1);
paramcount = strarray_length(params);
paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);

View File

@ -423,7 +423,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
/* SEND <file name> <address> <port> <size> [...] */
/* SEND <file name> <address> 0 <size> <id> (DCC SEND passive protocol) */
params = g_strsplit(data, " ", -1);
paramcount = strarray_length(params);
paramcount = g_strv_length(params);
if (paramcount < 4) {
signal_emit("dcc error ctcp", 5, "SEND", data,
@ -473,8 +473,8 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
net_ip2host(&temp_dcc->addr, temp_dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
strocpy(temp_dcc->addrstr, address,
sizeof(temp_dcc->addrstr));
g_strlcpy(temp_dcc->addrstr, address,
sizeof(temp_dcc->addrstr));
}
/* This new signal is added to let us invoke
@ -508,7 +508,7 @@ static void ctcp_msg_dcc_send(IRC_SERVER_REC *server, const char *data,
net_ip2host(&dcc->addr, dcc->addrstr);
else {
/* with IPv6, show it to us as it was sent */
strocpy(dcc->addrstr, address, sizeof(dcc->addrstr));
g_strlcpy(dcc->addrstr, address, sizeof(dcc->addrstr));
}
dcc->port = port;
dcc->size = size;
@ -526,14 +526,14 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
{
GET_DCC_REC *dcc;
GSList *tmp, *next;
char *nick, *fname;
char *nick, *arg, *fname;
void *free_arg;
int found;
g_return_if_fail(data != NULL);
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST,
&nick, &fname))
if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
PARAM_FLAG_STRIP_TRAILING_WS, &nick, &arg))
return;
if (*nick == '\0') {
@ -548,6 +548,8 @@ void cmd_dcc_receive(const char *data, DCC_GET_FUNC accept_func,
return;
}
fname = cmd_get_quoted_param(&arg);
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
GET_DCC_REC *dcc = tmp->data;

View File

@ -88,7 +88,7 @@ static int dcc_ctcp_resume_parse(int type, const char *data, const char *nick,
/* RESUME|ACCEPT <file name> <port> <size> */
/* RESUME|ACCEPT <file name> 0 <size> <id> (passive protocol) */
params = g_strsplit(data, " ", -1);
paramcount = strarray_length(params);
paramcount = g_strv_length(params);
if (paramcount < 3)
return 0;

View File

@ -245,7 +245,7 @@ static void dcc_server_msg(SERVER_DCC_REC *dcc, const char *msg)
/* 120 clientnickname filesize filename */
params = g_strsplit(msg, " ", -1);
paramcount = strarray_length(params);
paramcount = g_strv_length(params);
if (paramcount < 3) {
g_strfreev(params);

View File

@ -490,7 +490,7 @@ static void event_no_such_nick(IRC_SERVER_REC *server, char *data)
static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
{
GSList *tmp, *next;
char *typestr, *nick, *arg;
char *typestr, *nick, *arg, *fname;
void *free_arg;
int found, type;
@ -510,13 +510,15 @@ static void cmd_dcc_close(char *data, IRC_SERVER_REC *server)
return;
}
fname = cmd_get_quoted_param(&arg);
found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next) {
DCC_REC *dcc = tmp->data;
next = tmp->next;
if (dcc->type == type && g_ascii_strcasecmp(dcc->nick, nick) == 0 &&
(*arg == '\0' || g_strcmp0(dcc->arg, arg) == 0)) {
(*fname == '\0' || g_strcmp0(dcc->arg, fname) == 0)) {
dcc_reject(dcc, server);
found = TRUE;
}

View File

@ -31,6 +31,8 @@
#include "fe-common/core/printtext.h" /* FIXME: evil. need to do fe-proxy */
#include <sys/un.h>
GSList *proxy_listens;
GSList *proxy_clients;
@ -39,6 +41,66 @@ static int ignore_next;
static int enabled = FALSE;
static int is_all_digits(const char *s)
{
return strspn(s, "0123456789") == strlen(s);
}
static GIOChannel *net_listen_unix(const char *path)
{
struct sockaddr_un sa;
int saved_errno, handle;
g_return_val_if_fail(path != NULL, NULL);
handle = socket(AF_UNIX, SOCK_STREAM, 0);
if (handle == -1) {
return NULL;
}
fcntl(handle, F_SETFL, O_NONBLOCK);
memset(&sa, '\0', sizeof sa);
sa.sun_family = AF_UNIX;
strncpy(sa.sun_path, path, sizeof sa.sun_path - 1);
if (bind(handle, (struct sockaddr *)&sa, sizeof sa) == -1) {
saved_errno = errno;
goto error_close;
}
if (listen(handle, 1) == -1) {
saved_errno = errno;
goto error_unlink;
}
return g_io_channel_new(handle);
error_unlink:
unlink(sa.sun_path);
error_close:
close(handle);
errno = saved_errno;
return NULL;
}
static GIOChannel *net_accept_unix(GIOChannel *handle)
{
struct sockaddr_un sa;
int ret;
socklen_t addrlen;
g_return_val_if_fail(handle != NULL, NULL);
addrlen = sizeof sa;
ret = accept(g_io_channel_unix_get_fd(handle), (struct sockaddr *)&sa, &addrlen);
if (ret < 0)
return NULL;
fcntl(ret, F_SETFL, O_NONBLOCK);
return g_io_channel_new(ret);
}
static void remove_client(CLIENT_REC *rec)
{
g_return_if_fail(rec != NULL);
@ -48,18 +110,18 @@ static void remove_client(CLIENT_REC *rec)
signal_emit("proxy client disconnected", 1, rec);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d disconnected", rec->host, rec->port);
"Proxy: Client %s disconnected", rec->addr);
g_free(rec->proxy_address);
net_sendbuffer_destroy(rec->handle, TRUE);
g_source_remove(rec->recv_tag);
g_free_not_null(rec->nick);
g_free_not_null(rec->host);
g_free_not_null(rec->addr);
g_free(rec);
}
static void proxy_redirect_event(CLIENT_REC *client, const char *command,
int count, const char *arg, int remote)
int count, const char *arg, int remote)
{
char *str;
@ -67,7 +129,7 @@ static void proxy_redirect_event(CLIENT_REC *client, const char *command,
str = g_strdup_printf("proxy %p", client);
server_redirect_event(client->server, command, count,
arg, remote, NULL, "", str, NULL);
arg, remote, NULL, "", str, NULL);
g_free(str);
}
@ -94,28 +156,58 @@ static void grab_who(CLIENT_REC *client, const char *channel)
}
proxy_redirect_event(client, "who",
client->server->one_endofwho ? 1 : count,
arg->str, -1);
client->server->one_endofwho ? 1 : count,
arg->str, -1);
g_strfreev(list);
g_string_free(arg, TRUE);
}
static void handle_client_connect_cmd(CLIENT_REC *client,
const char *cmd, const char *args)
const char *cmd, const char *args)
{
const char *password;
password = settings_get_str("irssiproxy_password");
if (password != NULL && g_strcmp0(cmd, "PASS") == 0) {
if (g_strcmp0(password, args) == 0)
client->pass_sent = TRUE;
else {
if (g_strcmp0(cmd, "PASS") == 0) {
const char *args_pass;
if (!client->multiplex) {
args_pass = args;
} else {
IRC_SERVER_REC *server;
char *tag;
const char *tag_end;
if ((tag_end = strchr(args, ':')) != NULL) {
args_pass = tag_end + 1;
} else {
tag_end = args + strlen(args);
args_pass = "";
}
tag = g_strndup(args, tag_end - args);
server = IRC_SERVER(server_find_chatnet(tag));
g_free(tag);
if (!server) {
/* an invalid network was specified */
remove_client(client);
return;
}
client->server = server;
g_free(client->proxy_address);
client->proxy_address = g_strdup_printf("%.*s.proxy", (int)(tag_end - args), args);
}
if (g_strcmp0(password, args_pass) != 0) {
/* wrong password! */
remove_client(client);
return;
return;
}
client->pass_sent = TRUE;
} else if (g_strcmp0(cmd, "NICK") == 0) {
g_free_not_null(client->nick);
client->nick = g_strdup(args);
@ -124,14 +216,14 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
if (client->nick != NULL && client->user_sent) {
if (*password != '\0' && !client->pass_sent) {
if ((*password != '\0' || client->multiplex) && !client->pass_sent) {
/* client didn't send us PASS, kill it */
remove_client(client);
} else {
signal_emit("proxy client connected", 1, client);
printtext(client->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Client %s:%d connected",
client->host, client->port);
"Proxy: Client %s connected",
client->addr);
client->connected = TRUE;
proxy_dump_data(client);
}
@ -139,7 +231,7 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
}
static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
const char *data)
const char *data)
{
GSList *tmp;
if (!client->connected) {
@ -162,8 +254,8 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
g_ascii_strcasecmp(target, client->proxy_address) == 0 ||
g_ascii_strcasecmp(target, client->nick) == 0) {
proxy_outdata(client, ":%s PONG %s :%s\r\n",
client->proxy_address,
client->proxy_address, origin);
client->proxy_address,
client->proxy_address, origin);
g_free(params);
return;
}
@ -172,27 +264,27 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (g_strcmp0(cmd, "PROXY") == 0) {
if (g_ascii_strcasecmp(args, "CTCP ON") == 0) {
/* client wants all ctcps */
/* client wants all ctcps */
client->want_ctcp = 1;
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
if ((g_ascii_strcasecmp(client->listen->ircnet,rec->listen->ircnet) == 0) &&
/* kludgy way to check if the clients aren't the same */
(client->recv_tag != rec->recv_tag)) {
if (rec->want_ctcp == 1)
proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
rec->proxy_address, rec->nick, rec->listen->ircnet);
rec->want_ctcp = 0;
}
if (g_ascii_strcasecmp(client->listen->ircnet, rec->listen->ircnet) == 0 &&
/* kludgy way to check if the clients aren't the same */
client->recv_tag != rec->recv_tag) {
if (rec->want_ctcp == 1)
proxy_outdata(rec, ":%s NOTICE %s :Another client is now receiving CTCPs sent to %s\r\n",
rec->proxy_address, rec->nick, rec->listen->ircnet);
rec->want_ctcp = 0;
}
}
proxy_outdata(client, ":%s NOTICE %s :You're now receiving CTCPs sent to %s\r\n",
client->proxy_address, client->nick,client->listen->ircnet);
client->proxy_address, client->nick, client->listen->ircnet);
} else if (g_ascii_strcasecmp(args, "CTCP OFF") == 0) {
/* client wants proxy to handle all ctcps */
/* client wants proxy to handle all ctcps */
client->want_ctcp = 0;
proxy_outdata(client, ":%s NOTICE %s :Proxy is now handling itself CTCPs sent to %s\r\n",
client->proxy_address, client->nick, client->listen->ircnet);
client->proxy_address, client->nick, client->listen->ircnet);
} else {
signal_emit("proxy client command", 3, client, args, data);
}
@ -201,11 +293,11 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
if (client->server == NULL || !client->server->connected) {
proxy_outdata(client, ":%s NOTICE %s :Not connected to server\r\n",
client->proxy_address, client->nick);
return;
client->proxy_address, client->nick);
return;
}
/* check if the command could be redirected */
/* check if the command could be redirected */
if (g_strcmp0(cmd, "WHO") == 0)
grab_who(client, args);
else if (g_strcmp0(cmd, "WHOWAS") == 0)
@ -263,29 +355,29 @@ static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args,
char *params, *target, *msg;
params = event_get_params(args, 2 | PARAM_FLAG_GETREST,
&target, &msg);
&target, &msg);
proxy_outserver_all_except(client, "PRIVMSG %s", args);
ignore_next = TRUE;
if (*msg != '\001' || msg[strlen(msg)-1] != '\001') {
signal_emit(server_ischannel(SERVER(client->server), target) ?
"message own_public" : "message own_private", 4,
client->server, msg, target, target);
"message own_public" : "message own_private", 4,
client->server, msg, target, target);
} else if (strncmp(msg+1, "ACTION ", 7) == 0) {
/* action */
msg[strlen(msg)-1] = '\0';
msg[strlen(msg)-1] = '\0';
signal_emit("message irc own_action", 3,
client->server, msg+8, target);
client->server, msg+8, target);
} else {
/* CTCP */
/* CTCP */
char *p;
msg[strlen(msg)-1] = '\0';
p = strchr(msg, ' ');
if (p != NULL) *p++ = '\0'; else p = "";
if (p != NULL) *p++ = '\0'; else p = "";
signal_emit("message irc own_ctcp", 4,
client->server, msg+1, p, target);
client->server, msg+1, p, target);
}
ignore_next = FALSE;
g_free(params);
@ -337,24 +429,38 @@ static void sig_listen(LISTEN_REC *listen)
CLIENT_REC *rec;
IPADDR ip;
NET_SENDBUF_REC *sendbuf;
GIOChannel *handle;
GIOChannel *handle;
char host[MAX_IP_LEN];
int port;
char *addr;
g_return_if_fail(listen != NULL);
/* accept connection */
handle = net_accept(listen->handle, &ip, &port);
if (handle == NULL)
return;
net_ip2host(&ip, host);
if (listen->port) {
handle = net_accept(listen->handle, &ip, &port);
if (handle == NULL)
return;
net_ip2host(&ip, host);
addr = g_strdup_printf("%s:%d", host, port);
} else {
/* no port => this is a unix socket */
handle = net_accept_unix(listen->handle);
if (handle == NULL)
return;
addr = g_strdup("(local)");
}
sendbuf = net_sendbuffer_create(handle, 0);
rec = g_new0(CLIENT_REC, 1);
rec->listen = listen;
rec->handle = sendbuf;
rec->host = g_strdup(host);
rec->port = port;
if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->addr = addr;
if (g_strcmp0(listen->ircnet, "?") == 0) {
rec->multiplex = TRUE;
rec->proxy_address = g_strdup("multiplex.proxy");
rec->server = NULL;
} else if (g_strcmp0(listen->ircnet, "*") == 0) {
rec->proxy_address = g_strdup("irc.proxy");
rec->server = servers == NULL ? NULL : IRC_SERVER(servers->data);
} else {
@ -363,15 +469,15 @@ static void sig_listen(LISTEN_REC *listen)
IRC_SERVER(server_find_chatnet(listen->ircnet));
}
rec->recv_tag = g_input_add(handle, G_INPUT_READ,
(GInputFunction) sig_listen_client, rec);
(GInputFunction) sig_listen_client, rec);
proxy_clients = g_slist_prepend(proxy_clients, rec);
rec->listen->clients = g_slist_prepend(rec->listen->clients, rec);
listen->clients = g_slist_prepend(listen->clients, rec);
signal_emit("proxy client connecting", 1, rec);
printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: New client %s:%d on port %d (%s)",
rec->host, rec->port, listen->port, listen->ircnet);
"Proxy: New client %s on port %s (%s)",
rec->addr, listen->port_or_path, listen->ircnet);
}
static void sig_incoming(IRC_SERVER_REC *server, const char *line)
@ -460,12 +566,12 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
static void event_connected(IRC_SERVER_REC *server)
{
GSList *tmp;
const char *chatnet;
const char *chatnet;
if (!IS_IRC_SERVER(server))
return;
chatnet = server->connrec->chatnet;
chatnet = server->connrec->chatnet;
for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
CLIENT_REC *rec = tmp->data;
@ -474,7 +580,7 @@ static void event_connected(IRC_SERVER_REC *server)
(chatnet != NULL &&
g_ascii_strcasecmp(chatnet, rec->listen->ircnet) == 0))) {
proxy_outdata(rec, ":%s NOTICE %s :Connected to server\r\n",
rec->proxy_address, rec->nick);
rec->proxy_address, rec->nick);
rec->server = server;
proxy_client_reset_nick(rec);
}
@ -482,7 +588,7 @@ static void event_connected(IRC_SERVER_REC *server)
}
static void proxy_server_disconnected(CLIENT_REC *client,
IRC_SERVER_REC *server)
IRC_SERVER_REC *server)
{
GSList *tmp;
@ -567,14 +673,19 @@ static void sig_message_own_action(IRC_SERVER_REC *server, const char *msg,
proxy_outserver_all(server, "PRIVMSG %s :\001ACTION %s\001", target, msg);
}
static LISTEN_REC *find_listen(const char *ircnet, int port)
static LISTEN_REC *find_listen(const char *ircnet, int port, const char *port_or_path)
{
GSList *tmp;
for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
LISTEN_REC *rec = tmp->data;
if (rec->port == port &&
if ((port
? /* a tcp port */
rec->port == port
: /* a unix socket path */
g_strcmp0(rec->port_or_path, port_or_path) == 0
) &&
g_ascii_strcasecmp(rec->ircnet, ircnet) == 0)
return rec;
}
@ -582,48 +693,53 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
return NULL;
}
static void add_listen(const char *ircnet, int port)
static void add_listen(const char *ircnet, int port, const char *port_or_path)
{
LISTEN_REC *rec;
IPADDR ip4, ip6, *my_ip;
GIOChannel *handle;
if (port <= 0 || *ircnet == '\0')
if (*port_or_path == '\0' || port < 0 || *ircnet == '\0')
return;
/* bind to specific host/ip? */
my_ip = NULL;
if (*settings_get_str("irssiproxy_bind") != '\0') {
if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
&ip4, &ip6) != 0) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: can not resolve '%s' - aborting",
settings_get_str("irssiproxy_bind"));
return;
}
if (port == 0) {
/* listening on a unix socket */
handle = net_listen_unix(port_or_path);
} else {
/* bind to specific host/ip? */
my_ip = NULL;
if (*settings_get_str("irssiproxy_bind") != '\0') {
if (net_gethostbyname(settings_get_str("irssiproxy_bind"),
&ip4, &ip6) != 0) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: can not resolve '%s' - aborting",
settings_get_str("irssiproxy_bind"));
return;
}
my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 ||
settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4;
}
handle = net_listen(my_ip, &port);
}
if (handle == NULL) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: Listen in port %s failed: %s",
port_or_path, g_strerror(errno));
return;
}
rec = g_new0(LISTEN_REC, 1);
rec->handle = handle;
rec->ircnet = g_strdup(ircnet);
rec->port = port;
rec->handle = net_listen(my_ip, &rec->port);
if (rec->handle == NULL) {
printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
"Proxy: Listen in port %d failed: %s",
rec->port, g_strerror(errno));
g_free(rec->ircnet);
g_free(rec);
return;
}
rec->port_or_path = g_strdup(port_or_path);
rec->tag = g_input_add(rec->handle, G_INPUT_READ,
(GInputFunction) sig_listen, rec);
(GInputFunction) sig_listen, rec);
proxy_listens = g_slist_append(proxy_listens, rec);
proxy_listens = g_slist_append(proxy_listens, rec);
}
static void remove_listen(LISTEN_REC *rec)
@ -633,8 +749,13 @@ static void remove_listen(LISTEN_REC *rec)
while (rec->clients != NULL)
remove_client(rec->clients->data);
/* remove unix socket because bind wants to (re)create it */
if (rec->port == 0)
unlink(rec->port_or_path);
net_disconnect(rec->handle);
g_source_remove(rec->tag);
g_free(rec->port_or_path);
g_free(rec->ircnet);
g_free(rec);
}
@ -644,7 +765,7 @@ static void read_settings(void)
LISTEN_REC *rec;
GSList *remove_listens = NULL;
GSList *add_listens = NULL;
char **ports, **tmp, *ircnet, *port;
char **ports, **tmp, *ircnet, *port_or_path;
int portnum;
remove_listens = g_slist_copy(proxy_listens);
@ -652,20 +773,25 @@ static void read_settings(void)
ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
for (tmp = ports; *tmp != NULL; tmp++) {
ircnet = *tmp;
port = strchr(ircnet, '=');
if (port == NULL)
port_or_path = strchr(ircnet, '=');
if (port_or_path == NULL)
continue;
*port++ = '\0';
portnum = atoi(port);
if (portnum <= 0)
continue;
*port_or_path++ = '\0';
if (is_all_digits(port_or_path)) {
portnum = atoi(port_or_path);
if (portnum <= 0)
continue;
} else {
portnum = 0;
}
rec = find_listen(ircnet, portnum);
rec = find_listen(ircnet, portnum, port_or_path);
if (rec == NULL) {
rec = g_new0(LISTEN_REC, 1);
rec->ircnet = ircnet; /* borrow */
rec->port = portnum;
rec->port_or_path = port_or_path; /* borrow */
add_listens = g_slist_prepend(add_listens, rec);
} else {
/* remove from the list of listens to remove == keep it */
@ -680,7 +806,7 @@ static void read_settings(void)
while (add_listens != NULL) {
rec = add_listens->data;
add_listen(rec->ircnet, rec->port);
add_listen(rec->ircnet, rec->port, rec->port_or_path);
add_listens = g_slist_remove(add_listens, rec);
g_free(rec);
}

View File

@ -28,13 +28,14 @@
/* SYNTAX: IRSSIPROXY STATUS */
static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
{
GSList *tmp;
if (!settings_get_bool("irssiproxy")) {
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy is currently disabled");
return;
}
GSList *tmp;
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
"Proxy: Currently connected clients: %d",
@ -44,10 +45,10 @@ static void cmd_irssiproxy_status(const char *data, IRC_SERVER_REC *server)
CLIENT_REC *rec = tmp->data;
printtext(server, NULL, MSGLEVEL_CLIENTNOTICE,
" %s:%d connect%s to %d (%s)",
rec->host, rec->port,
" %s connect%s to %s (%s)",
rec->addr,
rec->connected ? "ed" : "ing",
rec->listen->port, rec->listen->ircnet);
rec->listen->port_or_path, rec->listen->ircnet);
}
}

View File

@ -9,17 +9,18 @@
typedef struct {
int port;
char *port_or_path;
char *ircnet;
int tag;
GIOChannel *handle;
GSList *clients;
} LISTEN_REC;
typedef struct {
char *nick, *host;
int port;
char *nick, *addr;
NET_SENDBUF_REC *handle;
int recv_tag;
char *proxy_address;
@ -29,6 +30,7 @@ typedef struct {
unsigned int user_sent:1;
unsigned int connected:1;
unsigned int want_ctcp:1;
unsigned int multiplex:1;
} CLIENT_REC;
#endif

View File

@ -117,16 +117,26 @@ textui_sources = \
EXTRA_DIST = \
get-signals.pl \
irssi-core.pl \
Makefile_silent.pm \
$(common_sources) \
$(irc_sources) \
$(ui_sources) \
$(textui_sources)
am_v_pl__show_gen = $(am__v_pl__show_gen_$(V))
am_v_pl__hide_gen = $(am__v_pl__hide_gen_$(V))
am__v_pl__show_gen_ = $(am__v_pl__show_gen_$(AM_DEFAULT_VERBOSITY))
am__v_pl__hide_gen_ = $(am__v_pl__hide_gen_$(AM_DEFAULT_VERBOSITY))
am__v_pl__show_gen_0 = echo " GEN " $$dir ;
am__v_pl__hide_gen_0 = > /dev/null
am__v_pl__show_gen_1 =
am__v_pl__hide_gen_1 =
all-local:
for dir in $(perl_dirs); do \
$(AM_V_GEN)for dir in $(perl_dirs); do \
cd $$dir && \
if [ ! -f Makefile ]; then \
$(perlpath) Makefile.PL $(PERL_MM_PARAMS); \
$(am_v_pl__show_gen)$(perlpath) Makefile.PL $(PERL_MM_PARAMS) $(am_v_pl__hide_gen); \
fi && \
($(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS) || \
$(MAKE) CC="$(CC)" CCFLAGS="$(PERL_CFLAGS) $(CFLAGS)" $(PERL_EXTRA_OPTS)) && \

View File

@ -0,0 +1,76 @@
push @ExtUtils::MakeMaker::Overridable, qw(pm_to_blib);
my $verb = $AM_DEFAULT_VERBOSITY;
{ package MY;
sub _center {
my $z = shift;
(length $z == 2 ? " $z " : length $z == 4 ? " $z " : " $z ").' '
}
sub _silent_cmd {
my $z = shift;
$z =~ s{\t(?:- ?)?\K(?=\$\((?|(CC)CMD|(XS)UBPPRUN|(LD|MV|CHMOD)|(RM)_R?F|(CP)_NONEMPTY|FULL_(AR)\)))}{\$(PL_AM_V_$1)}g;
$z
}
sub c_o { _silent_cmd(shift->SUPER::c_o(@_)) }
sub xs_c { _silent_cmd(shift->SUPER::xs_c(@_)) }
sub xs_o { _silent_cmd(shift->SUPER::xs_o(@_)) }
sub dynamic_lib { _silent_cmd(shift->SUPER::dynamic_lib(@_)) }
sub static_lib { _silent_cmd(shift->SUPER::static_lib(@_)) }
sub dynamic_bs {
my $ret = shift->SUPER::dynamic_bs(@_);
$ret =~ s{Running Mkbootstrap for}{\$(PL_AM_V_BS_Text)}g;
_silent_cmd($ret)
}
sub pm_to_blib {
my $ret = shift->SUPER::pm_to_blib(@_);
$ret =~ s{^(\t(?:- ?)?)(?:\$\(NOECHO\) ?)?(.*-e ['"]pm_to_blib(.*\\\n)*.*)$}{$1\$(PL_AM_V_BLIB)$2\$(PL_AM_V_BLIB_Hide)}mg;
$ret
}
sub post_constants {
my $ret = shift->SUPER::post_constants(@_);
my @terse = qw(cc xs ld chmod cp ar blib);
my @silent = qw(mv rm);
my @special = qw(BLIB_Hide);
#default verbosity from command line parameter
$ret .= "
AM_DEFAULT_VERBOSITY = @{[$verb ? 1 : 0]}
";
#default options forward
$ret .= "
PL_AM_V_${_} = \$(pl_am__v_${_}_\$(V))
pl_am__v_${_}_ = \$(pl_am__v_${_}_\$(AM_DEFAULT_VERBOSITY))
" for @special, map uc, @terse, @silent;
#quoted plain text needs extra quotes
$ret .= "
PL_AM_V_BS_Text = \"\$(pl_am__v_BS_Text_\$(V))\"
pl_am__v_BS_Text_ = \$(pl_am__v_BS_Text_\$(AM_DEFAULT_VERBOSITY))
"
#hide pm_to_blib output
. "
pl_am__v_BLIB_Hide_0 = \$(DEV_NULL)
pl_am__v_BLIB_Hide_1 =
"
#text for Mkbootstrap
. "
pl_am__v_BS_Text_0 = \"@{[_center('BS')]}\"
pl_am__v_BS_Text_1 = \"Running Mkbootstrap for\"
";
#"terse" output
$ret .= "
pl_am__v_${_}_0 = \$(NOECHO)echo \"@{[_center($_)]}\" \$\@;
" for map uc, @terse;
#no output
$ret .= "
pl_am__v_${_}_0 = \$(NOECHO)
" for map uc, @silent;
#in verbose mode the "terse" echo expands to nothing
$ret .= "
pl_am__v_${_}_1 =
" for map uc, @terse, @silent;
$ret
}
}
1;

View File

@ -74,15 +74,20 @@ static char *perl_expando_event(PerlExpando *rec, SERVER_REC *server,
ret = NULL;
if (SvTRUE(ERRSV)) {
PERL_SCRIPT_REC *script = rec->script;
(void) POPs;
/* call putback before emitting script error signal as that
* could manipulate the perl stack. */
PUTBACK;
/* make sure we don't get back here */
if (rec->script != NULL)
script_unregister_expandos(rec->script);
if (script != NULL)
script_unregister_expandos(script);
/* rec has been freed now */
signal_emit("script error", 2, rec->script, SvPV_nolen(ERRSV));
char *error = g_strdup(SvPV_nolen(ERRSV));
signal_emit("script error", 2, script, error);
g_free(error);
} else if (retcount > 0) {
ret = g_strdup(POPp);
*free_ret = TRUE;

View File

@ -1,4 +1,4 @@
use ExtUtils::MakeMaker;
use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "../Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi',
'LIBS' => '',

View File

@ -149,14 +149,14 @@ static void perl_notifylist_fill_hash(HV *hv, NOTIFYLIST_REC *notify)
static void perl_client_fill_hash(HV *hv, CLIENT_REC *client)
{
(void) hv_store(hv, "nick", 4, new_pv(client->nick), 0);
(void) hv_store(hv, "host", 4, new_pv(client->host), 0);
(void) hv_store(hv, "port", 4, newSViv(client->port), 0);
(void) hv_store(hv, "addr", 4, new_pv(client->addr), 0);
(void) hv_store(hv, "proxy_address", 13, new_pv(client->proxy_address), 0);
(void) hv_store(hv, "server", 6, iobject_bless(client->server), 0);
(void) hv_store(hv, "pass_sent", 9, newSViv(client->pass_sent), 0);
(void) hv_store(hv, "user_sent", 9, newSViv(client->user_sent), 0);
(void) hv_store(hv, "connected", 9, newSViv(client->connected), 0);
(void) hv_store(hv, "want_ctcp", 9, newSViv(client->want_ctcp), 0);
(void) hv_store(hv, "multiplex", 9, newSViv(client->multiplex), 0);
(void) hv_store(hv, "ircnet", 6, new_pv(client->listen->ircnet), 0);
}

View File

@ -1,4 +1,4 @@
use ExtUtils::MakeMaker;
use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "../Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::Irc',
'LIBS' => '',

View File

@ -393,7 +393,7 @@ int perl_get_api_version(void)
return IRSSI_PERL_API_VERSION;
}
static void perl_scripts_autorun(void)
void perl_scripts_autorun(void)
{
DIR *dirp;
struct dirent *dp;

View File

@ -16,6 +16,8 @@ extern GSList *perl_scripts;
void perl_scripts_init(void);
/* Destroy all perl scripts and deinitialize perl interpreter */
void perl_scripts_deinit(void);
/* Load all the scripts in the autorun/ folder */
void perl_scripts_autorun(void);
/* Load a perl script, path must be a full path. */
PERL_SCRIPT_REC *perl_script_load_file(const char *path);

View File

@ -119,8 +119,20 @@ static void cmd_script_unload(const char *data)
static void cmd_script_reset(const char *data)
{
void *free_arg;
GHashTable *optlist;
if (!cmd_get_params(data, &free_arg, 0 | PARAM_FLAG_OPTIONS,
"script reset", &optlist))
return;
perl_scripts_deinit();
perl_scripts_init();
if (g_hash_table_lookup(optlist, "autorun") != NULL)
perl_scripts_autorun();
cmd_params_free(free_arg);
}
static void cmd_script_list(void)
@ -251,6 +263,7 @@ void fe_perl_init(void)
command_bind("script list", NULL, (SIGNAL_FUNC) cmd_script_list);
command_bind("load", NULL, (SIGNAL_FUNC) cmd_load);
command_set_options("script exec", "permanent");
command_set_options("script reset", "autorun");
signal_add("script error", (SIGNAL_FUNC) sig_script_error);
signal_add("complete command script load", (SIGNAL_FUNC) sig_complete_load);

View File

@ -433,8 +433,9 @@ static void perl_signal_remove_list_one(GSList **siglist, PERL_SIGNAL_REC *rec)
}
#define sv_func_cmp(f1, f2) \
(f1 == f2 || (SvPOK(f1) && SvPOK(f2) && \
g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
((SvROK(f1) && SvROK(f2) && SvRV(f1) == SvRV(f2)) || \
(SvPOK(f1) && SvPOK(f2) && \
g_strcmp0(SvPV_nolen(f1), SvPV_nolen(f2)) == 0))
static void perl_signal_remove_list(GSList **list, SV *func)
{

View File

@ -1,4 +1,4 @@
use ExtUtils::MakeMaker;
use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "../Makefile_silent.pm";
WriteMakefile('NAME' => 'Irssi::TextUI',
'LIBS' => '',

View File

@ -77,7 +77,10 @@ static void perl_statusbar_event(char *function, SBAR_ITEM_REC *item,
/* make sure we don't get back here */
script_unregister_statusbars(script);
}
signal_emit("script error", 2, script, SvPV_nolen(ERRSV));
char *error = g_strdup(SvPV_nolen(ERRSV));
signal_emit("script error", 2, script, error);
g_free(error);
} else {
/* min_size and max_size can be changed, move them to SBAR_ITEM_REC */
hv = hvref(item_sv);

Some files were not shown because too many files have changed in this diff Show More