LES on 24 Feb 2007 - Here is the situation: I use
Tkabber to chat on MSN,
ICQ,
IRC etc., but sometimes I feel like using SIM [
1] [
2], another multi-platform instant messenger, for the following reasons:
- it can send and receive files;
- it uses the protocols/servers directly while
Tkabber forces me to use
Jabber "bridges" that go down and break my connection a bit too often;
- it displays smileys correctly - that really shouldn't matter, but people insist on posting smileys and they're a bit tired of me asking "what was that funny string you just sent me?";
- it has OSD and that saves me a lot of the Alt+Tabbing that
Tkabber often forces me to do just to discover that that last comment from the other party was particularly not alt-tab-worthy;
- SIM looks a lot better overall than
Tkabber.
Two things have made me stick to
Tkabber:
1) Knowing that I can hack into
Tkabber's source because it is written in a language I know always gives me that warm fuzzy feeling.
2) I actually have hacked into
Tkabber because I absolutely refuse to chat without a decent "auto correct" implementation. I have discussed this here already (see:
AutoCorrect).
SIM does have "auto correct", even if it's just a half decent implementation. It is a plugin called "Replace text". It doesn't react to punctuation, but it reacts to space and, well, that's better than nothing. The biggest hurdle is the management interface. Although it offers a nice GUI for insertion and removal of entries, it offers no way to import an existing auto correct list. That has to be a problem because the auto correct list I have been using for eight years and currently in at least three applications has 13,595 entries, no less. I really need an import feature. But SIM has to keep its own list somewhere, doesn't it? I certainly can edit it, can't I? Well, I ran into a few other hurdles:
1) I keep my auto correct list centralized in one single file. That list is updated very often, so I need to be able to automate this synchronization with the SIM plugin configuration file. SIM keeps the configuration of ALL its plugins in one single file, in a "Replace" section, in a very similar arrangement to that of
Windows' .ini files [
3]. I use
Linux, but I imagine that SIM started on
Windows and came to
Linux afterwards. So, editing that file non-interactively requires some care with a lot of other data that is kept in the same file.
2) My auto correct list is neat [
4]: key=value, one pair in each line. Very easy to parse. SIM's Replace plugin stores its aliases in two lists: ALL THE KEYS followed by ALL THE VALUES. Additionally, each KEY must be accompanied by its corresponding number that serves as some sort of primary key of a hash or database-like structure. Accordingly, every VALUE must be accompanied by that number, matching the number of its corresponding KEY.
3) Finally, the second line of that "Replace" section must declare the number of auto correct entries. So I have to count them so I can update that line correctly.
This is it. You know the motive, you know the weapon, now here is the crime:
#!/usr/bin/env tclsh
# Modify the following with the file that contains your auto correct files:
set ::ALIASFILE "/home/$env(USER)/autocorrect.txt"
# Modify the following with the file that contains your SIM config file:
set ::SIMFILE "/home/$env(USER)/.kde/share/apps/sim/MSN.someuser@hotmail.com.1/plugins.conf"
# WARNING
# This script should work correctly on any Linux or Unix-like operating
# system, but probably won't work on other platforms without a few tweaks
# which I currently have no motivation to apply and test etc.
# Do not modify anything below this point unless you know what you're doing,
# i.e. altering the source.
# ================================================
# GLOBALS used in this script:
# ::ALIASFILE ::SIMFILE ::SIMKEYS ::SIMVALUES
# ::LINE1 ::LINE2 ::NEWCONFIG
# This script doesn't have any procs so everything is global, actually.
# I just created these "globals" to make them more visible than other vars.
# ================================================
# ================================================
# PARSE ALIASFILE:
# open ALIASFILE and parse each line. If the line matches a certain
# regular expression, the regex itself will pick the "key" and the "value",
# respectively. They're added to two Tcl lists: SIMKEYS and SIMVALUES.
# Finally, the number of aliases is counted.
# ================================================
set _aliascount 0
set _regex {([^=]+)\s*=\s*(.*)}
set _fp [ open $::ALIASFILE r ]
while { [ eof $_fp ] == 0 } {
set _line [ string trim [ gets $_fp ] ]
# # Exclude empty lines from the alias file
if { [ regexp {^$} $_line ] } { continue }
# # Exclude commented out lines in the alias file
if { [ regexp {^#.*$} $_line ] } { continue }
regexp $_regex $_line => _key _value
# # For example: absm=absolutamente; key=value
lappend ::SIMKEYS [ string trim $_key ]
lappend ::SIMVALUES [ string trim $_value ]
incr _aliascount
}
close $_fp
# We'll need to know the number of aliases in the next section
set ::LINE2 "Keys=$_aliascount"
# ================================================
# PARSE SIMFILE:
# open SIMFILE and parse each line.
# If line == [replace], this marks the beginning of the section we are going
# to "rewrite", or rather buffer in the NEWCONFIG global variable. So we
# buffer the "[replace]" mark (it is lost if not added explicitly) plus two
# required lines after that: LINE1 is whatever comes right after the
# "[replace]" mark. LINE2 is an indicator of the number of aliases the
# Replace plug-in should expect. Then we build the new list of keys and
# values and buffer that too. Finally, we flag up _ignore. That means we're
# parsing (and ignoring) old alias lines. If _ignore is flagged up, a regular
# expression tries to determine when the old alias section is over. When it
# is over, _ignore is flagged down again so the next iteration drops right
# to the last and most frequently used option (Option 3), which is adding the
# current line to NEWCONFIG.
# ================================================
set _ignore "no"
set _fp [ open $::SIMFILE r ]
while { [ eof $_fp ] == 0 } {
set _line [ string trim [ gets $_fp ] ]
# # Option 1
# # CONFIG starts at the line with "[replace]"
if { $_line == {[replace]} } {
append ::NEWCONFIG "\[replace\]\n"
# # The very next line is required
set ::LINE1 [ string trim [ gets $_fp ] ]
append ::NEWCONFIG "$::LINE1\n"
append ::NEWCONFIG "$::LINE2\n"
for { set i 0 } { $i < [ llength $::SIMKEYS ] } { incr i } {
set k [ expr {$i + 1} ]
append ::NEWCONFIG "Key=${k},\"[ lindex $::SIMKEYS $i ]\"\n"
}
for { set i 0 } { $i < [ llength $::SIMVALUES ] } { incr i } {
set k [ expr {$i + 1} ]
append ::NEWCONFIG "Value=${k},\"[ lindex $::SIMVALUES $i ]\"\n"
}
set _ignore "yes"
continue
}
# # Option 2
# # CONFIG ends at the line with "[something else]"
if { $_ignore == "yes" } {
if {[ regexp {^\[.+?\]$} $_line => ]} {
append ::NEWCONFIG "$_line\n"
set _ignore "no"
continue
}
}
# # ignore existing alias section
if { $_ignore == "yes" } { continue }
# # Option 3
append ::NEWCONFIG "$_line\n"
}; # end while
close $_fp
# ----------------------------------------------------------------
# Overwrite SIMFILE with the NEWCONFIG "buffer".
set _fp [ open $::SIMFILE w ]
puts $_fp $::NEWCONFIG
close $_fp
puts "done!"
exit