diff options
author | ailin-nemui <ailin-nemui@users.noreply.github.com> | 2016-09-21 15:59:33 +0200 |
---|---|---|
committer | ailin-nemui <ailin-nemui@users.noreply.github.com> | 2016-09-21 15:59:33 +0200 |
commit | ac73255483a42851db6a54ae4f0d5040e94608a2 (patch) | |
tree | 23c38bc6224171640d44be3269053948dd1a92e8 | |
parent | 13f4026ae0f0d5422f3163576d4c2eff8754176a (diff) | |
parent | d9b4cb34dccd7fb77e1d222333f3018ff947899c (diff) | |
download | irssi-ac73255483a42851db6a54ae4f0d5040e94608a2.zip |
Merge branch 'master' into integrate/0.8.20
101 files changed, 1622 insertions, 2616 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..e1ae7d9d --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.travis.yml b/.travis.yml index 46f58766..804dc58c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 @@ -97,3 +97,5 @@ Other patches (grep for "patch" in ChangeLog) by: Paul Johnson KindOne Fabian Kurz + Todd Pratt + xavierog @@ -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: @@ -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) diff --git a/acconfig.h b/acconfig.h deleted file mode 100644 index 9ace70d8..00000000 --- a/acconfig.h +++ /dev/null @@ -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 diff --git a/configure.ac b/configure.ac index 458c8aa9..ee03eb82 100644 --- a/configure.ac +++ b/configure.ac @@ -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 - - 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 +if test "x$want_textui" != "xno"; then + + 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 diff --git a/docs/help/in/channel.in b/docs/help/in/channel.in index 01bea3c1..86e824c2 100644 --- a/docs/help/in/channel.in +++ b/docs/help/in/channel.in @@ -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 diff --git a/docs/help/in/dcc.in b/docs/help/in/dcc.in index dac31e58..18a77ee9 100644 --- a/docs/help/in/dcc.in +++ b/docs/help/in/dcc.in @@ -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 diff --git a/docs/help/in/hilight.in b/docs/help/in/hilight.in index cd91560b..0726e5e7 100644 --- a/docs/help/in/hilight.in +++ b/docs/help/in/hilight.in @@ -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. diff --git a/docs/help/in/network.in b/docs/help/in/network.in index 2918f6ae..ba08ef69 100644 --- a/docs/help/in/network.in +++ b/docs/help/in/network.in @@ -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 diff --git a/docs/help/in/script.in b/docs/help/in/script.in index 6e94ef7f..e90135ad 100644 --- a/docs/help/in/script.in +++ b/docs/help/in/script.in @@ -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. diff --git a/docs/help/in/server.in b/docs/help/in/server.in index e407b6a9..68a62e2d 100644 --- a/docs/help/in/server.in +++ b/docs/help/in/server.in @@ -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 diff --git a/docs/irssi.1 b/docs/irssi.1 index f38b639e..e1fab0d8 100644 --- a/docs/irssi.1 +++ b/docs/irssi.1 @@ -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 diff --git a/docs/proxy.txt b/docs/proxy.txt index e6360a82..224d24e3 100644 --- a/docs/proxy.txt +++ b/docs/proxy.txt @@ -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: diff --git a/docs/startup-HOWTO.html b/docs/startup-HOWTO.html index 12128c59..07a9f47c 100644 --- a/docs/startup-HOWTO.html +++ b/docs/startup-HOWTO.html @@ -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> diff --git a/m4/curses.m4 b/m4/curses.m4 deleted file mode 100644 index 41c0e6c8..00000000 --- a/m4/curses.m4 +++ /dev/null @@ -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 -]) - diff --git a/src/common.h b/src/common.h index 729049ab..0a7b72f0 100644 --- a/src/common.h +++ b/src/common.h @@ -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 diff --git a/src/core/Makefile.am b/src/core/Makefile.am index fc32e17e..af323234 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -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) diff --git a/src/core/channels.c b/src/core/channels.c index 9af8b844..9c3b92ba 100644 --- a/src/core/channels.c +++ b/src/core/channels.c @@ -233,23 +233,46 @@ static int match_nick_flags(SERVER_REC *server, NICK_REC *nick, char flag) void channel_send_autocommands(CHANNEL_REC *channel) { CHANNEL_SETUP_REC *rec; - NICK_REC *nick; - char **bots, **bot; g_return_if_fail(IS_CHANNEL(channel)); if (channel->session_rejoin) - return; + 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); - return; } +} + +void channel_send_botcommands(CHANNEL_REC *channel) +{ + CHANNEL_SETUP_REC *rec; + NICK_REC *nick; + char **bots, **bot; + + 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; + + /* 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); diff --git a/src/core/channels.h b/src/core/channels.h index 0839d69b..bd136fe2 100644 --- a/src/core/channels.h +++ b/src/core/channels.h @@ -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); diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 8e881679..a9404fa3 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -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); diff --git a/src/core/commands.h b/src/core/commands.h index 72105ad3..93d6276b 100644 --- a/src/core/commands.h +++ b/src/core/commands.h @@ -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' */ diff --git a/src/core/ignore.c b/src/core/ignore.c index 8d5a27c2..2047dc9d 100644 --- a/src/core/ignore.c +++ b/src/core/ignore.c @@ -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 */ diff --git a/src/core/log.c b/src/core/log.c index 8306d2df..6af1effc 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -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); } diff --git a/src/core/misc.c b/src/core/misc.c index aaa470f5..bc9f504e 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -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; diff --git a/src/core/misc.h b/src/core/misc.h index 7e78d3b9..c095e131 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -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); diff --git a/src/core/network.c b/src/core/network.c index c3ad4e23..3e1b7c70 100644 --- a/src/core/network.c +++ b/src/core/network.c @@ -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 */ diff --git a/src/core/network.h b/src/core/network.h index 73ac8dee..03f3b813 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -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); diff --git a/src/core/rawlog.c b/src/core/rawlog.c index 875c0ef2..5927e730 100644 --- a/src/core/rawlog.c +++ b/src/core/rawlog.c @@ -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); } diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index f419035b..58c9dd09 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -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); diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index 4a048282..0cecfece 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -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); diff --git a/src/core/servers.c b/src/core/servers.c index 3342304e..d6297c4d 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -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; } diff --git a/src/core/settings.c b/src/core/settings.c index 8e493124..e65ceb2c 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -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)) { diff --git a/src/core/settings.h b/src/core/settings.h index 6f2cf129..d174f250 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -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); diff --git a/src/core/special-vars.c b/src/core/special-vars.c index 4dcc3d2f..6ca080fc 100644 --- a/src/core/special-vars.c +++ b/src/core/special-vars.c @@ -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; } diff --git a/src/core/special-vars.h b/src/core/special-vars.h index 11262dad..300dae0e 100644 --- a/src/core/special-vars.h +++ b/src/core/special-vars.h @@ -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, diff --git a/src/core/utf8.c b/src/core/utf8.c new file mode 100644 index 00000000..c53d8816 --- /dev/null +++ b/src/core/utf8.c @@ -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; +} diff --git a/src/core/utf8.h b/src/core/utf8.h new file mode 100644 index 00000000..5bb53193 --- /dev/null +++ b/src/core/utf8.h @@ -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 diff --git a/src/fe-common/core/wcwidth.c b/src/core/wcwidth.c index 80d20fa1..711c4646 100644 --- a/src/fe-common/core/wcwidth.c +++ b/src/core/wcwidth.c @@ -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; diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am index e755b510..63f91fa6 100644 --- a/src/fe-common/core/Makefile.am +++ b/src/fe-common/core/Makefile.am @@ -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 diff --git a/src/fe-common/core/chat-completion.c b/src/fe-common/core/chat-completion.c index c37c77cd..3e144602 100644 --- a/src/fe-common/core/chat-completion.c +++ b/src/fe-common/core/chat-completion.c @@ -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); diff --git a/src/fe-common/core/completion.c b/src/fe-common/core/completion.c index 4461de92..76dfbb79 100644 --- a/src/fe-common/core/completion.c +++ b/src/fe-common/core/completion.c @@ -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)); + } + } } } diff --git a/src/fe-common/core/fe-channels.c b/src/fe-common/core/fe-channels.c index 046d641a..00aac885 100644 --- a/src/fe-common/core/fe-channels.c +++ b/src/fe-common/core/fe-channels.c @@ -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)); - - formatnum = rec->op ? TXT_NAMES_NICK_OP : - rec->halfop ? TXT_NAMES_NICK_HALFOP : - rec->voice ? TXT_NAMES_NICK_VOICE : - TXT_NAMES_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; 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); diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c index ee7f9424..1b2ab1e2 100644 --- a/src/fe-common/core/fe-common-core.c +++ b/src/fe-common/core/fe-common-core.c @@ -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; } diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c index 7b1d7b94..97a246ec 100644 --- a/src/fe-common/core/fe-core-commands.c +++ b/src/fe-common/core/fe-core-commands.c @@ -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; diff --git a/src/fe-common/core/fe-exec.c b/src/fe-common/core/fe-exec.c index b5f289ce..36990866 100644 --- a/src/fe-common/core/fe-exec.c +++ b/src/fe-common/core/fe-exec.c @@ -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; } diff --git a/src/fe-common/core/fe-ignore.c b/src/fe-common/core/fe-ignore.c index a809ac91..52b11e6b 100644 --- a/src/fe-common/core/fe-ignore.c +++ b/src/fe-common/core/fe-ignore.c @@ -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); } diff --git a/src/fe-common/core/fe-log.c b/src/fe-common/core/fe-log.c index f2c4c014..5bc5c4e1 100644 --- a/src/fe-common/core/fe-log.c +++ b/src/fe-common/core/fe-log.c @@ -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; diff --git a/src/fe-common/core/fe-messages.c b/src/fe-common/core/fe-messages.c index 8ad83753..487a5754 100644 --- a/src/fe-common/core/fe-messages.c +++ b/src/fe-common/core/fe-messages.c @@ -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; diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index 429e6dac..468cb707 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -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); diff --git a/src/fe-common/core/fe-settings.c b/src/fe-common/core/fe-settings.c index b689cbf9..abbd45a8 100644 --- a/src/fe-common/core/fe-settings.c +++ b/src/fe-common/core/fe-settings.c @@ -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 : diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index d9a51201..9aa7698d 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -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; } diff --git a/src/fe-common/core/hilight-text.c b/src/fe-common/core/hilight-text.c index b746f636..4691a708 100644 --- a/src/fe-common/core/hilight-text.c +++ b/src/fe-common/core/hilight-text.c @@ -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); diff --git a/src/fe-common/core/hilight-text.h b/src/fe-common/core/hilight-text.h index 3c897def..ae05e1ca 100644 --- a/src/fe-common/core/hilight-text.h +++ b/src/fe-common/core/hilight-text.h @@ -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; diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c index e6d32b6d..5c07f14c 100644 --- a/src/fe-common/core/module-formats.c +++ b/src/fe-common/core/module-formats.c @@ -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 }, diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h index 3f06bb97..2b45ff6b 100644 --- a/src/fe-common/core/module-formats.h +++ b/src/fe-common/core/module-formats.h @@ -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, diff --git a/src/fe-common/core/module.h b/src/fe-common/core/module.h index 51b61b3e..db712ec7 100644 --- a/src/fe-common/core/module.h +++ b/src/fe-common/core/module.h @@ -2,7 +2,7 @@ #define MODULE_NAME "fe-common/core" -typedef guint32 unichar; +#include "utf8.h" typedef struct { time_t time; char *nick; diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c index 58e7b557..2b1459be 100644 --- a/src/fe-common/core/themes.c +++ b/src/fe-common/core/themes.c @@ -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) { diff --git a/src/fe-common/core/utf8.c b/src/fe-common/core/utf8.c deleted file mode 100644 index 2d07ea8e..00000000 --- a/src/fe-common/core/utf8.c +++ /dev/null @@ -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" - diff --git a/src/fe-common/core/utf8.h b/src/fe-common/core/utf8.h deleted file mode 100644 index 3c15dc7d..00000000 --- a/src/fe-common/core/utf8.h +++ /dev/null @@ -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 diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c index 2cb99a2f..2e22d6f2 100644 --- a/src/fe-common/irc/fe-irc-server.c +++ b/src/fe-common/irc/fe-irc-server.c @@ -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) diff --git a/src/fe-common/irc/fe-ircnet.c b/src/fe-common/irc/fe-ircnet.c index 4d7037d5..b70a9ea7 100644 --- a/src/fe-common/irc/fe-ircnet.c +++ b/src/fe-common/irc/fe-ircnet.c @@ -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); } diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am index b9538e60..bdd3df22 100644 --- a/src/fe-text/Makefile.am +++ b/src/fe-text/Makefile.am @@ -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) diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index 306141ec..f05decd2 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -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) diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h index 29d8dea2..8777f083 100644 --- a/src/fe-text/gui-entry.h +++ b/src/fe-text/gui-entry.h @@ -2,11 +2,15 @@ #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 diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index af560894..7c71edd7 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -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); diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c index cad271c9..ad79e0c4 100644 --- a/src/fe-text/irssi.c +++ b/src/fe-text/irssi.c @@ -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(); - - /* don't check settings with dummy mode */ - settings_check(); - } + 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(); + + 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(); - - term_refresh_thaw(); - term_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(); 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 */ diff --git a/src/fe-text/lastlog.c b/src/fe-text/lastlog.c index 166d2847..c0b1dde8 100644 --- a/src/fe-text/lastlog.c +++ b/src/fe-text/lastlog.c @@ -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; diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c index e3e0c2a6..0db4f63a 100644 --- a/src/fe-text/statusbar-items.c +++ b/src/fe-text/statusbar-items.c @@ -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"); diff --git a/src/fe-text/statusbar.c b/src/fe-text/statusbar.c index 5448243a..f0dff828 100644 --- a/src/fe-text/statusbar.c +++ b/src/fe-text/statusbar.c @@ -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); diff --git a/src/fe-text/term-curses.c b/src/fe-text/term-curses.c deleted file mode 100644 index 752edd7f..00000000 --- a/src/fe-text/term-curses.c +++ /dev/null @@ -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)++; - } -} diff --git a/src/fe-text/term-dummy.c b/src/fe-text/term-dummy.c deleted file mode 100644 index 6a56af88..00000000 --- a/src/fe-text/term-dummy.c +++ /dev/null @@ -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); -} diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c index 27be904e..b2478c62 100644 --- a/src/fe-text/term-terminfo.c +++ b/src/fe-text/term-terminfo.c @@ -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) diff --git a/src/fe-text/term.h b/src/fe-text/term.h index 9b726d82..0c7847f6 100644 --- a/src/fe-text/term.h +++ b/src/fe-text/term.h @@ -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; diff --git a/src/fe-text/terminfo-core.c b/src/fe-text/terminfo-core.c index 15b00081..9c9179a5 100644 --- a/src/fe-text/terminfo-core.c +++ b/src/fe-text/terminfo-core.c @@ -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); diff --git a/src/fe-text/tparm.c b/src/fe-text/tparm.c deleted file mode 100644 index 97c790da..00000000 --- a/src/fe-text/tparm.c +++ /dev/null @@ -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; -} diff --git a/src/irc/core/irc-channels-setup.c b/src/irc/core/irc-channels-setup.c index 2320352d..bbbc095c 100644 --- a/src/irc/core/irc-channels-setup.c +++ b/src/irc/core/irc-channels-setup.c @@ -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); } diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c index bcb9d1f6..b22f3269 100644 --- a/src/irc/core/irc-nicklist.c +++ b/src/irc/core/irc-nicklist.c @@ -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); diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index f905a862..8148997b 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -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", ""); diff --git a/src/irc/dcc/dcc-chat.c b/src/irc/dcc/dcc-chat.c index e3dbe72d..ca90b8d8 100644 --- a/src/irc/dcc/dcc-chat.c +++ b/src/irc/dcc/dcc-chat.c @@ -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); diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c index a8b1c967..f7a95bb9 100644 --- a/src/irc/dcc/dcc-get.c +++ b/src/irc/dcc/dcc-get.c @@ -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; diff --git a/src/irc/dcc/dcc-resume.c b/src/irc/dcc/dcc-resume.c index 11b28aef..36f84ddf 100644 --- a/src/irc/dcc/dcc-resume.c +++ b/src/irc/dcc/dcc-resume.c @@ -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; diff --git a/src/irc/dcc/dcc-server.c b/src/irc/dcc/dcc-server.c index 30224ff9..7ae572cd 100644 --- a/src/irc/dcc/dcc-server.c +++ b/src/irc/dcc/dcc-server.c @@ -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); diff --git a/src/irc/dcc/dcc.c b/src/irc/dcc/dcc.c index 17f6c477..c51f7c7a 100644 --- a/src/irc/dcc/dcc.c +++ b/src/irc/dcc/dcc.c @@ -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; } diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c index 5dc9a704..cde4c0be 100644 --- a/src/irc/proxy/listen.c +++ b/src/irc/proxy/listen.c @@ -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; } + handle = net_listen(my_ip, &port); + } - my_ip = ip6.family == 0 ? &ip4 : ip4.family == 0 || - settings_get_bool("resolve_prefer_ipv6") ? &ip6 : &ip4; + 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); } diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c index 50a41b21..2875d2c2 100644 --- a/src/irc/proxy/proxy.c +++ b/src/irc/proxy/proxy.c @@ -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); } } diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h index 158b0675..620ea605 100644 --- a/src/irc/proxy/proxy.h +++ b/src/irc/proxy/proxy.h @@ -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 diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am index a09c49a5..427c5492 100644 --- a/src/perl/Makefile.am +++ b/src/perl/Makefile.am @@ -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)) && \ diff --git a/src/perl/Makefile_silent.pm b/src/perl/Makefile_silent.pm new file mode 100644 index 00000000..b5d71d66 --- /dev/null +++ b/src/perl/Makefile_silent.pm @@ -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; diff --git a/src/perl/common/Expando.xs b/src/perl/common/Expando.xs index e8e8f751..26800b05 100644 --- a/src/perl/common/Expando.xs +++ b/src/perl/common/Expando.xs @@ -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; diff --git a/src/perl/common/Makefile.PL.in b/src/perl/common/Makefile.PL.in index 4e545e7c..84a80403 100644 --- a/src/perl/common/Makefile.PL.in +++ b/src/perl/common/Makefile.PL.in @@ -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' => '', diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs index db5c5f79..3f8ccc2e 100644 --- a/src/perl/irc/Irc.xs +++ b/src/perl/irc/Irc.xs @@ -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); } diff --git a/src/perl/irc/Makefile.PL.in b/src/perl/irc/Makefile.PL.in index 8f1e94d5..0fbc5241 100644 --- a/src/perl/irc/Makefile.PL.in +++ b/src/perl/irc/Makefile.PL.in @@ -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' => '', diff --git a/src/perl/perl-core.c b/src/perl/perl-core.c index cb690906..2c61df70 100644 --- a/src/perl/perl-core.c +++ b/src/perl/perl-core.c @@ -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; diff --git a/src/perl/perl-core.h b/src/perl/perl-core.h index b451cc5c..7390a6fd 100644 --- a/src/perl/perl-core.h +++ b/src/perl/perl-core.h @@ -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); diff --git a/src/perl/perl-fe.c b/src/perl/perl-fe.c index 04305b63..396c80b7 100644 --- a/src/perl/perl-fe.c +++ b/src/perl/perl-fe.c @@ -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); diff --git a/src/perl/perl-signals.c b/src/perl/perl-signals.c index 007f7ad5..8f993660 100644 --- a/src/perl/perl-signals.c +++ b/src/perl/perl-signals.c @@ -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) { diff --git a/src/perl/textui/Makefile.PL.in b/src/perl/textui/Makefile.PL.in index 9e80274b..3541f75c 100644 --- a/src/perl/textui/Makefile.PL.in +++ b/src/perl/textui/Makefile.PL.in @@ -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' => '', diff --git a/src/perl/textui/Statusbar.xs b/src/perl/textui/Statusbar.xs index a449e11d..8b0e5f65 100644 --- a/src/perl/textui/Statusbar.xs +++ b/src/perl/textui/Statusbar.xs @@ -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); diff --git a/src/perl/ui/Makefile.PL.in b/src/perl/ui/Makefile.PL.in index a349918e..ed87d528 100644 --- a/src/perl/ui/Makefile.PL.in +++ b/src/perl/ui/Makefile.PL.in @@ -1,4 +1,4 @@ -use ExtUtils::MakeMaker; +use ExtUtils::MakeMaker;our $AM_DEFAULT_VERBOSITY='@AM_DEFAULT_VERBOSITY@';require "../Makefile_silent.pm"; WriteMakefile('NAME' => 'Irssi::UI', 'LIBS' => '', |