#!/usr/bin/perl ######################################################################## # Xspell install instructions: # Linux/UNIX users: Place xspell.pl in your ~/.xchat or ~/.xchat2 # directory. # Windows users: Place in your (Xchat 1.x - All windows version) # C:\Program Files\xchat\data\ or (XChat 2 on WinNT/2K/XP) # %HOMEDRIVE%%HOMEPATH%\Application Data\X-Chat 2\ # (Not sure for Xchat 2 on Windows 9x/ME). # # Xspell provides an optional spell check function for xchat. # # Notes: All 'global' variables are in hash form for easier tracking. # Now Win32 compatible (0.2.0+) # # The script looks for ispell, however any command-line spell checker # should work. Available on my website is a Win32 version of the GNU # ispell. Extract (unzip) and place it in c:\ispell\ Linux/UNIX users # install your distro's ispell and ispell-en* packages. *Replace with # your locale as necessary. # # Linux/UNIX users, this uses xterm to run ispell in, please ensure you # have xterm installed or fill in ain alternate value for the term # variable below. (don't forget the -e switch: term => 'myterm -e') # # Some terminals do not exit with the command fed via -e. This is a # function of the terminal program and not this script. xterm does exit # when ispell has completed. # # Copyright (C) 2000-2003 Gatewood Green # http://woody.linif.org/ ######################################################################## ##### use declarations ################################################# package Xspell; use warnings; use IO::File; use Encode; use encoding 'utf8'; ##### Global variables ################################################# # Global variables containing script info %scriptinfo = ( 'name' => 'Xspell', 'version' => '0.5.0', 'author' => 'Woody Green', 'email' => 'woody@linif.org', 'url' => 'http://woody.linif.org/xchat/', ); # Make adjustments here %config = ( 'spell' => 1, 'spellmsg' => 1, 'spellchar' => '', 'speller' => '', 'term' => '', 'encoding' => 'iso-8859-1', 'diag' => 0 ); %errors = (); $errors{'config'} = 0; %temp = (); $temp{'config'} = IRC::get_info( 4 ) . "/Xspell.config"; # Default I/O encoding $encoding = 'utf8'; &Xspell::readconfig( ); &Xspell::setencoding( ); # Defaults and tmp paths. Leave these alone and make pref adjustments above if ( $^O eq 'MSWin32' ) { if ( length( $ENV{TMP} ) ) { $temp{'tmp'} = $ENV{TMP}; } else { $temp{'tmp'} = 'c:\windows\temp'; } $config{'speller'} = 'c:\ispell\ispell.exe' unless length( $config{'speller'} ); } else { $temp{'tmp'} = '/tmp'; unless ( length( $config{'speller'} ) ) { if ( -e "/usr/bin/ispell" || -e "/usr/local/bin/ispell" ) { $config{'speller'} = 'ispell'; } elsif ( -e "/usr/bin/aspell" || -e "/usr/local/bin/aspell" ) { $config{'speller'} = 'aspell -c'; } else { IRC::print( "\002(Xspell) I can't find a spell checking program!!! Please install ispell.\002" ); } } unless ( length( $config{'term'} ) ) { if ( -e "/usr/X11R6/bin/xterm" ) { $config{'term'} = 'xterm'; } elsif ( -e "/usr/X11R6/bin/rxvt" || -e "/usr/bin/rxvt" || -e "/usr/local/bin/rxvt" ) { $config{'term'} = 'rxvt'; } elsif ( -e "/usr/X11R6/bin/aterm" || -e "/usr/bin/aterm" || -e "/usr/local/bin/aterm" ) { $config{'term'} = 'aterm'; } elsif ( -e "/usr/X11R6/bin/Eterm" || -e "/usr/bin/Eterm" || -e "/usr/local/bin/Eterm" ) { $config{'term'} = 'Eterm'; } else { IRC::print( "\002(Xspell) I can't find a terminal!!!\002" ); } } } ##### Script setup ##################################################### IRC::print ( "\0034--------------------------------------------\003\n" . "Loading \002" . $scriptinfo{'name'} . "\002 version: \002" . $scriptinfo{'version'} . "\002\n" . " By: " . $scriptinfo{'author'} . " \002<\002" . $scriptinfo{'email'} . "\002>\002\n" . " " . $scriptinfo{'url'} . "\n" . " \n" . " /spell to enable/disable spell checking\n" . " /spellmsg to enable/disable spell checking for /msg\n" . " /spellchar for 'flag' mode. eg: /spellchar #\n" . " /spellterm to set terminal. eg: /spellterm xterm\n" . " /speller to set spell checking program. eg: /speller ispell\n" . " /xspellsave to save preferences.\n" . " \n" . " To edit default on or off, see:\n" . " \"# Make adjustments here\"\n" . " at the top ~/.xchat/xspell.pl\n" . "\0034--------------------------------------------\003\n" ); IRC::register( $scriptinfo{'name'}, $scriptinfo{'version'}, 'Xspell::cleanup', '' ); IRC::add_command_handler( 'SPELL' , 'Xspell::handler_command_spell' ); # Toggle spell checking on and off IRC::add_command_handler( 'SPELLMSG' , 'Xspell::handler_command_spellmsg' ); # Toggle spell checking /msg on and off IRC::add_command_handler( 'SPELLCHAR' , 'Xspell::handler_command_spellchar' ); # Set spell checking indicator IRC::add_command_handler( 'SPELLTERM' , 'Xspell::handler_command_spellterm' ); # Set spell checking terminal program IRC::add_command_handler( 'SPELLER' , 'Xspell::handler_command_speller' ); # Set spell checking program IRC::add_command_handler( 'SPELLENCODE' , 'Xspell::handler_command_spellencode' ); # Set text file I/O encoding IRC::add_command_handler( '' , 'Xspell::handler_command_privmsg' ); # Normal input IRC::add_command_handler( 'MSG' , 'Xspell::handler_command_privmsg_msg' ); # Spurious /msg IRC::add_command_handler( 'XSPELLSAVE' , 'Xspell::handler_command_xspellsave' ); # Save config to file IRC::add_command_handler( 'SPELLVARS' , 'Xspell::handler_command_diagnostic_dumpvars' ); IRC::add_command_handler( 'SPELLDIAG' , 'Xspell::handler_command_diagnostic_diag' ); ##### Script functions ################################################# sub handler_command_privmsg ( ) { my $msg = shift; IRC::print( "Diag1: $msg" ) if $config{'diag'}; if ( $config{'spell'} && ( $config{'spellchar'} eq '' || $msg =~ s/$config{'spellchar'}$// ) ) { IRC::print( "Diag2: $msg" ) if $config{'diag'}; $msg =~ s/\s*$//; # Remove any trailing spaces $msg =~ s/$config{'spellchar'}$// if ( $config{'spellchar'} ne '' ); # Remove spell flag if used # Create a unique filename so that we can converse in # many channels at once and spell check safely my $filename = IRC::get_info( 2 ) . time( ); $filename =~ s/^#//; # Remove the channel's leading "#" my $fh = new IO::File or IRC::print( "Failed to get filehandle." ); # Use IO::File to avoid file handle collisions if ($fh->open( "$temp{'tmp'}/$filename.xspell", ">:$encoding" ) ) { IRC::print( "Diag3: $msg" ) if $config{'diag'}; print $fh $msg; $fh->close( ); } else { IRC::print( "Failed to open tmp file." ); } if ( $^O eq 'MSWin32' ) { system( "copy $temp{'tmp'}\\$filename.xspell $temp{'tmp'}\\$filename.xspell.bak" ) if $config{'diag'}; system( "$config{'speller'} $temp{'tmp'}\\$filename.xspell" ); } else { system( "cp $temp{'tmp'}/$filename.xspell $temp{'tmp'}/$filename.xspell.bak" ) if $config{'diag'}; system( "$config{'term'} -e $config{'speller'} -x $temp{'tmp'}/$filename.xspell" ); } $msg = ""; if ($fh->open( "$temp{'tmp'}/$filename.xspell", "<:$encoding" ) ) { while ( <$fh> ) { $msg .= $_; } IRC::print( "Diag4: $msg" ) if $config{'diag'}; $fh->close( ); } chomp( $msg ); my $channel = IRC::get_info( 2 ); my $nick = IRC::get_info( 1 ); IRC::print( "\0036\<\003$nick\0036\>\003\t$msg" ); IRC::command( "/privmsg $channel :$msg" ); unlink( "$temp{'tmp'}/$filename.xspell" ) unless $config{'diag'}; unlink( "$temp{'tmp'}/$filename.xspell.bak" ) unless $config{'diag'}; return 1; # Stop xchat from processing msg } return 0; # Let xchat continue processing msg } sub handler_command_privmsg_msg ( ) { my $msg = shift; if ( $config{'spell'} && $config{'spellmsg'} && $msg !~ /^(X(\@channels.undernet.org){0,1}|(nick|chan)serv)/i && ( $config{'spellchar'} eq '' || $msg =~ s/$config{'spellchar'}$// ) ) { $msg =~ s/\s*$//; # Remove any trailing spaces $msg =~ s/$config{'spellchar'}$// if ( $config{'spellchar'} ne '' ); # Remove spell flag if used my $split = index( $msg, " " ); my $target = substr( $msg, 0, $split, "" ); $msg =~ s/^\s+//; # Create a unique filename so that we can converse in # many channels at once and spell check safely my $filename = IRC::get_info( 2 ) . time( ); $filename =~ s/^#//; # Remove the channel's leading "#" my $fh = new IO::File; if ($fh->open( "$temp{'tmp'}/$filename.xspell", ">:$encoding" ) ) { print $fh $msg; $fh->close( ); } if ( $^O eq 'MSWin32' ) { system( "$config{'speller'} $temp{'tmp'}\\$filename.xspell" ); } else { system( "$config{'term'} -e $config{'speller'} -x $temp{'tmp'}/$filename.xspell" ); } $msg = ""; if ($fh->open( "$temp{'tmp'}/$filename.xspell", "<:$encoding" ) ) { while ( <$fh> ) { $msg .= $_; } $fh->close( ); } chomp( $msg ); my $nick = IRC::get_info( 1 ); IRC::print( "\0036\<\003$nick\0036\>\003\t$msg" ); IRC::command( "/privmsg $target :$msg" ); unlink( "$temp{'tmp'}/$filename.xspell" ) unless $config{'diag'}; unlink( "$temp{'tmp'}/$filename.xspell.bak" ) unless $config{'diag'}; return 1; } return 0; } sub handler_command_spell ( ) { if ( $config{'spell'} ) { $config{'spell'} = 0; IRC::print( "Spell checking OFF.\n" ); } else { $config{'spell'} = 1; IRC::print( "Spell checking ON.\n " ); } return 1; } sub handler_command_spellmsg ( ) { if ( $config{'spellmsg'} ) { $config{'spellmsg'} = 0; IRC::print( "Spell checking for /msg OFF.\n" ); } else { $config{'spellmsg'} = 1; IRC::print( "Spell checking for /msg ON.\n " ); } return 1; } sub handler_command_spellchar ( ) { my $arg = shift; if ( $arg ) { my $char = substr( $arg, 0, 1 ); $config{'spellchar'} = $char; IRC::print( "Spell checking char (flag) set to '$char'. " . "Spell checking will only be done if you end your input with '$char'.\n" ); } else { $config{'spellchar'} = ''; IRC::print( "Spell checking set for all input.\n" ); } return 1; } sub handler_command_speller ( ) { my $arg = shift; if ( $arg ) { $config{'speller'} = $arg; IRC::print( "Spell checking program set to '$arg'.\n" ); } return 1; } sub handler_command_spellterm ( ) { my $arg = shift; if ( $arg ) { $config{'term'} = $arg; IRC::print( "Spell checking terminal program set to '$arg'.\n" ); } return 1; } sub handler_command_spellencode ( ) { my $arg = shift; # Get actual encoding type (also has the side benefit of determining if # the desired encoding is valid for the local system) $arg = Encode::resolve_alias( $arg ); if ( $arg ) { $config{'encoding'} = $arg; &Xspell::setencoding( ); IRC::print( "Spell checking I/O encoding set to '$arg'.\n" ); } else { IRC::print( "Invalid encoding.\n" ); } return 1; } sub setencoding ( ) { $config{'encoding'} =~ s/\s//g; use encoding 'utf8', STDIN => $config{'encoding'}, STDOUT => $config{'encoding'}; if ( $config{'encoding'} =~ /^utf8$/ ) { $encoding = $config{'encoding'}; } else { $encoding = 'encoding(' . $config{'encoding'} . ')'; } return 0; } sub readconfig ( ) { if ( -e $temp{'config'} ) { open CONFIG, "<" . $temp{'config'} or Xspell::configerror( "READ" ); if ( !$errors{'config'} ) { while ( ) { $_ =~ s/\s+/ /g; $_ =~ s/^\s//; $_ =~ s/^#.*$//; if ( length( $_ ) > 1 ) { my ( $var, $val ) = split " = ", $_; $config{$var} = $val; } } close (CONFIG); } } # Clean up prefs from older script version. We now hard code program options for simplicity. $config{'term'} =~ s/\s-e//; $config{'speller'} =~ s/\s-x//; return 0; } sub writeconfig ( ) { if ( !$errors{'config'} ) { open CONFIG, ">" . $temp{'config'} or Xspell::configerror( "WRITE" ); if ( !$errors{'config'} ) { print CONFIG "# Xspell config file.\n"; print CONFIG "# Line style: variable = value\n"; print CONFIG "# #'s indicate coments.\n\n"; foreach my $key ( keys %config ) { print CONFIG "$key = " . $config{$key} . "\n" if $key ne 'diag'; } close (CONFIG); } } return 0; } sub configerror ( ) { if ( $_[0] =~ /READ/i ) { IRC::print( "Config file found but inaccesable.\n" ); $errors{'config'} = 1; } elsif ( $_[0] =~ /WRITE/i ) { IRC::print( "Could not open config file for writing.\n" ); $errors{'config'} = 2; } return 0; } sub handler_command_xspellsave ( ) { if ( $_[0] =~ /HELP/i ) { IRC::print ( "/xspellsave\n" . " Saves the current script configuration and seen data.\n" ); } else { Xspell::writeconfig( ); IRC::print( "Xspell settings saved." ); } return 1; } sub cleanup ( ) { # If xchat autosaves, so do we if ( IRC::get_prefs( "autosave" ) ) { &Xspell::writeconfig( ); } return 0; } sub handler_command_diagnostic_dumpvars ( ) { # Diagnostic - Show current global variable values IRC::print( "Dumping Xspell variables...\n" ); for my $var ( 'scriptinfo', 'config', 'errors', 'temp' ) { my $count = keys( %{$var} ); IRC::print( "Dumping $count elements of $var...\n" ); for my $key ( keys %{$var} ) { IRC::print( " \$$var { $key } = " . $$var{$key} . "\n" ); } } IRC::print( "\n\$encoding = $encoding \n\n" ); IRC::print( "Finished dumping Xspell variables.\n" ); return 1; } sub handler_command_diagnostic_diag ( ) { if ( $config{'diag'} == 0 ) { $config{'diag'} = 1; } else { $config{'diag'} = 0; } return 1; }