summaryrefslogtreecommitdiffstats
authorRaghavendra D Prabhu <[email protected]>2010-10-17 21:18:29 (GMT)
committer Raghavendra D Prabhu <[email protected]>2010-10-17 21:18:29 (GMT)
commit9dacbb1960add439f6d2b8a3a587b0613d4798f9 (patch) (side-by-side diff)
treedab8f90c530c6846361f3dd52943271867c40c85
downloadweescripts-9dacbb1960add439f6d2b8a3a587b0613d4798f9.zip
weescripts-9dacbb1960add439f6d2b8a3a587b0613d4798f9.tar.gz
weescripts-9dacbb1960add439f6d2b8a3a587b0613d4798f9.tar.bz2
Initial commit
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--README1
-rw-r--r--buffers.pl336
-rw-r--r--grep.py1583
-rw-r--r--notify.py117
4 files changed, 2037 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..d8c7653
--- a/dev/null
+++ b/README
@@ -0,0 +1 @@
+some scripts I modified/improved  I
diff --git a/buffers.pl b/buffers.pl
new file mode 100644
index 0000000..155bf62
--- a/dev/null
+++ b/buffers.pl
@@ -0,0 +1,336 @@
+#
+# Copyright (c) 2008-2010 by FlashCode <[email protected]>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# Display sidebar with list of buffers.
+#
+# History:
+# 2010-04-12, FlashCode <[email protected]>:
+# v1.9: replace call to log() by length() to align buffer numbers
+# 2010-04-02, FlashCode <[email protected]>:
+# v1.8: fix bug with background color and option indenting_number
+# 2010-04-02, Helios <[email protected]>:
+# v1.7: add indenting_number option
+# 2010-02-25, m4v <[email protected]>:
+# v1.6: add option to hide empty prefixes
+# 2010-02-12, FlashCode <[email protected]>:
+# v1.5: add optional nick prefix for buffers like IRC channels
+# 2009-09-30, FlashCode <[email protected]>:
+# v1.4: remove spaces for indenting when bar position is top/bottom
+# 2009-06-14, FlashCode <[email protected]>:
+# v1.3: add option "hide_merged_buffers"
+# 2009-06-14, FlashCode <[email protected]>:
+# v1.2: improve display with merged buffers
+# 2009-05-02, FlashCode <[email protected]>:
+# v1.1: sync with last API changes
+# 2009-02-21, FlashCode <[email protected]>:
+# v1.0: remove timer used to update bar item first time (not needed any more)
+# 2009-02-17, FlashCode <[email protected]>:
+# v0.9: fix bug with indenting of private buffers
+# 2009-01-04, FlashCode <[email protected]>:
+# v0.8: update syntax for command /set (comments)
+# 2008-10-20, Jiri Golembiovsky <[email protected]>:
+# v0.7: add indenting option
+# 2008-10-01, FlashCode <[email protected]>:
+# v0.6: add default color for buffers, and color for current active buffer
+# 2008-09-18, FlashCode <[email protected]>:
+# v0.5: fix color for "low" level entry in hotlist
+# 2008-09-18, FlashCode <[email protected]>:
+# v0.4: rename option "show_category" to "short_names",
+# remove option "color_slash"
+# 2008-09-15, FlashCode <[email protected]>:
+# v0.3: fix bug with priority in hotlist (var not defined)
+# 2008-09-02, FlashCode <[email protected]>:
+# v0.2: add color for buffers with activity and config options for
+# colors, add config option to display/hide categories
+# 2008-03-15, FlashCode <[email protected]>:
+# v0.1: script creation
+#
+# Help about settings:
+# display short names (remove text before first "." in buffer name):
+# /set plugins.var.perl.buffers.short_names on
+# use indenting for some buffers like IRC channels:
+# /set plugins.var.perl.buffers.indenting on
+# use indenting for numbers:
+# /set plugins.var.perl.buffers.indenting_number on
+# hide merged buffers:
+# /set plugins.var.perl.buffers.hide_merged_buffers on
+# show prefix:
+# /set plugins.var.perl.buffers.show_prefix on
+# /set plugins.var.perl.buffers.show_prefix_empty on
+# change colors:
+# /set plugins.var.perl.buffers.color_number color
+# /set plugins.var.perl.buffers.color_default color
+# /set plugins.var.perl.buffers.color_hotlist_low color
+# /set plugins.var.perl.buffers.color_hotlist_message color
+# /set plugins.var.perl.buffers.color_hotlist_private color
+# /set plugins.var.perl.buffers.color_hotlist_highlight color
+# /set plugins.var.perl.buffers.color_current color
+# (replace "color" by your color, which may be "fg" or "fg,bg")
+#
+
+use strict;
+
+my $version = "1.9";
+
+# -------------------------------[ config ]-------------------------------------
+
+my %default_options = ("short_names" => "on",
+ "indenting" => "on",
+ "indenting_number" => "on",
+ "max_length" => "10",
+ "hide_merged_buffers" => "off",
+ "show_prefix" => "off",
+ "show_prefix_empty" => "on",
+ "color_hotlist_low" => "white",
+ "color_hotlist_message" => "yellow",
+ "color_hotlist_private" => "lightgreen",
+ "color_hotlist_highlight" => "magenta",
+ "color_current" => "lightcyan,red",
+ "color_default" => "default",
+ "color_number" => "lightgreen",
+ );
+my %options;
+my %hotlist_level = (0 => "low", 1 => "message", 2 => "private", 3 => "highlight");
+
+# --------------------------------[ init ]--------------------------------------
+
+weechat::register("buffers", "FlashCode <flashcode\@flashtux.org>", $version,
+ "GPL3", "Sidebar with list of buffers", "", "");
+
+foreach my $option (keys %default_options)
+{
+ if (!weechat::config_is_set_plugin($option))
+ {
+ weechat::config_set_plugin($option, $default_options{$option});
+ }
+}
+buffers_read_options();
+
+weechat::bar_item_new("buffers", "build_buffers", "");
+weechat::bar_new("buffers", "0", "0", "root", "", "left", "horizontal",
+ "vertical", "0", "0", "default", "default", "default", "1",
+ "buffers");
+weechat::hook_signal("buffer_*", "buffers_signal_buffer", "");
+weechat::hook_signal("hotlist_*", "buffers_signal_hotlist", "");
+weechat::hook_config("plugins.var.perl.buffers.*", "buffers_signal_config", "");
+weechat::bar_item_update("buffers");
+
+# ------------------------------------------------------------------------------
+
+sub buffers_read_options
+{
+ foreach my $option (keys %default_options)
+ {
+ $options{$option} = weechat::config_get_plugin($option);
+ }
+}
+
+sub build_buffers
+{
+ my $str = "";
+
+ # get bar position (left/right/top/bottom)
+ my $position = "left";
+ my $option_position = weechat::config_get("weechat.bar.buffers.position");
+ if ($option_position ne "")
+ {
+ $position = weechat::config_string($option_position);
+ }
+
+ # read hotlist
+ my %hotlist;
+ my $infolist = weechat::infolist_get("hotlist", "", "");
+ while (weechat::infolist_next($infolist))
+ {
+ $hotlist{weechat::infolist_pointer($infolist, "buffer_pointer")} =
+ weechat::infolist_integer($infolist, "priority");
+ }
+ weechat::infolist_free($infolist);
+
+ # read buffers list
+ my @buffers;
+ my @current1 = ();
+ my @current2 = ();
+ my $old_number = -1;
+ my $max_number = 0;
+ my $max_number_digits = 0;
+ my $active_seen = 0;
+ $infolist = weechat::infolist_get("buffer", "", "");
+ while (weechat::infolist_next($infolist))
+ {
+ my $buffer = {};
+ my $number = weechat::infolist_integer($infolist, "number");
+ if ($number ne $old_number)
+ {
+ @buffers = (@buffers, @current2, @current1);
+ @current1 = ();
+ @current2 = ();
+ $active_seen = 0;
+ }
+ if ($number > $max_number)
+ {
+ $max_number = $number;
+ }
+ $old_number = $number;
+ my $active = weechat::infolist_integer($infolist, "active");
+ if ($active)
+ {
+ $active_seen = 1;
+ }
+ $buffer->{"pointer"} = weechat::infolist_pointer($infolist, "pointer");
+ $buffer->{"number"} = $number;
+ $buffer->{"active"} = $active;
+ $buffer->{"current_buffer"} = weechat::infolist_integer($infolist, "current_buffer");
+ $buffer->{"short_name"} = weechat::infolist_string($infolist, "short_name");
+ $buffer->{"name"} = weechat::infolist_string($infolist, "name");
+ if ($active_seen)
+ {
+ push(@current2, $buffer);
+ }
+ else
+ {
+ push(@current1, $buffer);
+ }
+ }
+ if ($max_number >= 1)
+ {
+ $max_number_digits = length(int($max_number));
+ }
+ @buffers = (@buffers, @current2, @current1);
+ weechat::infolist_free($infolist);
+
+
+ # build string with buffers
+ $old_number = -1;
+ for my $buffer (@buffers)
+ {
+ if (($options{"hide_merged_buffers"} eq "on") && (! $buffer->{"active"}))
+ {
+ next;
+ }
+ my $color = $options{"color_default"};
+ $color = "default" if ($color eq "");
+ my $bg = "";
+ if (exists $hotlist{$buffer->{"pointer"}})
+ {
+ $color = $options{"color_hotlist_".$hotlist_level{$hotlist{$buffer->{"pointer"}}}};
+ }
+ if ($buffer->{"current_buffer"})
+ {
+ $color = $options{"color_current"};
+ $bg = $1 if ($color =~ /.*,(.*)/);
+ }
+ my $color_bg = "";
+ $color_bg = weechat::color(",".$bg) if ($bg ne "");
+ if (($options{"indenting_number"} eq "on")
+ && (($position eq "left") || ($position eq "right")))
+ {
+ $str .= weechat::color("default").$color_bg
+ .(" " x ($max_number_digits - length(int($buffer->{"number"}))));
+ }
+ if ($old_number ne $buffer->{"number"})
+ {
+ $str .= weechat::color($options{"color_number"})
+ .$color_bg
+ .$buffer->{"number"}
+ .weechat::color("default")
+ .$color_bg
+ .".";
+ }
+ else
+ {
+ my $indent = "";
+ $indent = ((" " x length($buffer->{"number"}))." ") if (($position eq "left") || ($position eq "right"));
+ $str .= weechat::color("default")
+ .$color_bg
+ .$indent;
+ }
+ if (($options{"indenting"} eq "on")
+ && (($position eq "left") || ($position eq "right")))
+ {
+ my $type = weechat::buffer_get_string($buffer->{"pointer"}, "localvar_type");
+ if (($type eq "channel") || ($type eq "private"))
+ {
+ $str .= " ";
+ }
+ }
+ if ($options{"show_prefix"} eq "on")
+ {
+ my $nickname = weechat::buffer_get_string($buffer->{"pointer"}, "localvar_nick");
+ if ($nickname ne "")
+ {
+ # with version >= 0.3.2, this infolist will return only nick
+ # with older versions, whole nicklist is returned for buffer, and this can be very slow
+ my $infolist_nick = weechat::infolist_get("nicklist", $buffer->{"pointer"}, "nick_".$nickname);
+ if ($infolist_nick ne "")
+ {
+ while (weechat::infolist_next($infolist_nick))
+ {
+ if ((weechat::infolist_string($infolist_nick, "type") eq "nick")
+ && (weechat::infolist_string($infolist_nick, "name") eq $nickname))
+ {
+ my $prefix = weechat::infolist_string($infolist_nick, "prefix");
+ if (($prefix ne " ") or ($options{"show_prefix_empty"} eq "on"))
+ {
+ $str .= weechat::color(weechat::config_color(
+ weechat::config_get(
+ weechat::infolist_string($infolist_nick, "prefix_color"))))
+ .$prefix;
+ }
+ last;
+ }
+ }
+ weechat::infolist_free($infolist_nick);
+ }
+ }
+ }
+ $str .= weechat::color($color);
+ if ($options{"short_names"} eq "on")
+ {
+ $str .= substr($buffer->{"short_name"},0,$options{"max_length"});
+ }
+ else
+ {
+ $str .= substr($buffer->{"name"},0,$options{"max_length"});
+ }
+ $str .= "\n";
+ $old_number = $buffer->{"number"};
+ }
+
+
+ return $str;
+}
+
+sub buffers_signal_buffer
+{
+ weechat::bar_item_update("buffers");
+ return weechat::WEECHAT_RC_OK;
+}
+
+sub buffers_signal_hotlist
+{
+ weechat::bar_item_update("buffers");
+ return weechat::WEECHAT_RC_OK;
+}
+
+sub buffers_signal_config
+{
+ buffers_read_options();
+ weechat::bar_item_update("buffers");
+ return weechat::WEECHAT_RC_OK;
+}
diff --git a/grep.py b/grep.py
new file mode 100644
index 0000000..aef4c82
--- a/dev/null
+++ b/grep.py
@@ -0,0 +1,1583 @@
+# -*- coding: utf-8 -*-
+###
+# Copyright (c) 2009-2010 by Elián Hanisch <[email protected]>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+###
+
+###
+# Search in Weechat buffers and logs (for Weechat 0.3.*)
+#
+# Inspired by xt's grep.py
+# Originally I just wanted to add some fixes in grep.py, but then
+# I got carried away and rewrote everything, so new script.
+#
+# Commands:
+# * /grep
+# Search in logs or buffers, see /help grep
+# * /logs:
+# Lists logs in ~/.weechat/logs, see /help logs
+#
+# Settings:
+# * plugins.var.python.grep.clear_buffer:
+# Clear the results buffer before each search. Valid values: on, off
+#
+# * plugins.var.python.grep.go_to_buffer:
+# Automatically go to grep buffer when search is over. Valid values: on, off
+#
+# * plugins.var.python.grep.log_filter:
+# Coma separated list of patterns that grep will use for exclude logs, e.g.
+# if you use '*server/*' any log in the 'server' folder will be excluded
+# when using the command '/grep log'
+#
+# * plugins.var.python.grep.show_summary:
+# Shows summary for each log. Valid values: on, off
+#
+# * plugins.var.python.grep.max_lines:
+# Grep will only print the last matched lines that don't surpass the value defined here.
+#
+# * plugins.var.python.grep.size_limit:
+# Size limit in KiB, is used for decide whenever grepping should run in background or not. If
+# the logs to grep have a total size bigger than this value then grep run as a new process.
+# It can be used for force or disable background process, using '0' forces to always grep in
+# background, while using '' (empty string) will disable it.
+#
+# * plugins.var.python.grep.default_tail_head:
+# Config option for define default number of lines returned when using --head or --tail options.
+# Can be overriden in the command with --number option.
+#
+#
+# TODO:
+# * try to figure out why hook_process chokes in long outputs (using a tempfile as a
+# workaround now)
+# * predefined regexp templates for common searches, like urls
+# * possibly add option for defining time intervals
+#
+#
+# History:
+# 2010-04-08
+# version 0.6.6: bug fixes
+# * use WEECHAT_LIST_POS_END in log file completion, makes completion faster
+# * disable bytecode if using python 2.6
+# * use single quotes in command string
+# * fix bug that could change buffer's title when using /grep stop
+#
+# 2010-01-24
+# version 0.6.5: disable bytecode is a 2.6 feature, instead, resort to delete the bytecode manually
+#
+# 2010-01-19
+# version 0.6.4: bug fix
+# version 0.6.3: added options --invert --only-match (replaces --exact, which is still available
+# but removed from help)
+# * use new 'irc_nick_color' info
+# * don't generate bytecode when spawning a new process
+# * show active options in buffer title
+#
+# 2010-01-17
+# version 0.6.2: removed 2.6-ish code
+# version 0.6.1: fixed bug when grepping in grep's buffer
+#
+# 2010-01-14
+# version 0.6.0: implemented grep in background
+# * improved context lines presentation.
+# * grepping for big (or many) log files runs in a weechat_process.
+# * added /grep stop.
+# * added 'size_limit' option
+# * fixed a infolist leak when grepping buffers
+# * added 'default_tail_head' option
+# * results are sort by line count
+# * don't die if log is corrupted (has NULL chars in it)
+# * changed presentation of /logs
+# * log path completion doesn't suck anymore
+# * removed all tabs, because I learned how to configure Vim so that spaces aren't annoying
+# anymore. This was the script's original policy.
+#
+# 2010-01-05
+# version 0.5.5: rename script to 'grep.py' (FlashCode <[email protected]>).
+#
+# 2010-01-04
+# version 0.5.4.1: fix index error when using --after/before-context options.
+#
+# 2010-01-03
+# version 0.5.4: new features
+# * added --after-context and --before-context options.
+# * added --context as a shortcut for using both -A -B options.
+#
+# 2009-11-06
+# version 0.5.3: improvements for long grep output
+# * grep buffer input accepts the same flags as /grep for repeat a search with different
+# options.
+# * tweaks in grep's output.
+# * max_lines option added for limit grep's output.
+# * code in update_buffer() optimized.
+# * time stats in buffer title.
+# * added go_to_buffer config option.
+# * added --buffer for search only in buffers.
+# * refactoring.
+#
+# 2009-10-12, omero
+# version 0.5.2: made it python-2.4.x compliant
+#
+# 2009-08-17
+# version 0.5.1: some refactoring, show_summary option added.
+#
+# 2009-08-13
+# version 0.5: rewritten from xt's grep.py
+# * fixed searching in non weechat logs, for cases like, if you're
+# switching from irssi and rename and copy your irssi logs to %h/logs
+# * fixed "timestamp rainbow" when you /grep in grep's buffer
+# * allow to search in other buffers other than current or in logs
+# of currently closed buffers with cmd 'buffer'
+# * allow to search in any log file in %h/logs with cmd 'log'
+# * added --count for return the number of matched lines
+# * added --matchcase for case sensible search
+# * added --hilight for color matches
+# * added --head and --tail options, and --number
+# * added command /logs for list files in %h/logs
+# * added config option for clear the buffer before a search
+# * added config option for filter logs we don't want to grep
+# * added the posibility to repeat last search with another regexp by writing
+# it in grep's buffer
+# * changed spaces for tabs in the code, which is my preference
+#
+###
+
+import sys, getopt, time, os
+path = os.path
+stat = os.stat
+
+try:
+ import weechat
+ WEECHAT_RC_OK = weechat.WEECHAT_RC_OK
+ import_ok = True
+except ImportError:
+ import_ok = False
+
+SCRIPT_NAME = "grep"
+SCRIPT_AUTHOR = "Elián Hanisch <[email protected]>"
+SCRIPT_VERSION = "0.6.6"
+SCRIPT_LICENSE = "GPL3"
+SCRIPT_DESC = "Search in buffers and logs"
+SCRIPT_COMMAND = "grep"
+
+### Default Settings ###
+settings = {
+'clear_buffer' : 'off',
+'log_filter' : '',
+'go_to_buffer' : 'on',
+'max_lines' : '4000',
+'show_summary' : 'on',
+'size_limit' : '2048',
+'default_tail_head' : '10',
+}
+
+### Class definitions ###
+class linesDict(dict):
+ """
+ Class for handling matched lines in more than one buffer.
+ linesDict[buffer_name] = matched_lines_list
+ """
+ def __setitem__(self, key, value):
+ assert isinstance(value, list)
+ if key not in self:
+ dict.__setitem__(self, key, value)
+ else:
+ dict.__getitem__(self, key).extend(value)
+
+ def get_matches_count(self):
+ """Return the sum of total matches stored."""
+ if dict.__len__(self):
+ return sum(map(lambda L: L.matches_count, self.itervalues()))
+ else:
+ return 0
+
+ def __len__(self):
+ """Return the sum of total lines stored."""
+ if dict.__len__(self):
+ return sum(map(len, self.itervalues()))
+ else:
+ return 0
+
+ def __str__(self):
+ """Returns buffer count or buffer name if there's just one stored."""
+ n = len(self.keys())
+ if n == 1:
+ return self.keys()[0]
+ elif n > 1:
+ return '%s logs' %n
+ else:
+ return ''
+
+ def items(self):
+ """Returns a list of items sorted by line count."""
+ items = dict.items(self)
+ items.sort(key=lambda i: len(i[1]))
+ return items
+
+ def items_count(self):
+ """Returns a list of items sorted by match count."""
+ items = dict.items(self)
+ items.sort(key=lambda i: i[1].matches_count)
+ return items
+
+ def strip_separator(self):
+ for L in self.itervalues():
+ L.strip_separator()
+
+ def get_last_lines(self, n):
+ total_lines = len(self)
+ #debug('total: %s n: %s' %(total_lines, n))
+ if n >= total_lines:
+ # nothing to do
+ return
+ for k, v in reversed(self.items()):
+ l = len(v)
+ if n > 0:
+ if l > n:
+ del v[:l-n]
+ v.stripped_lines = l-n
+ n -= l
+ else:
+ del v[:]
+ v.stripped_lines = l
+
+class linesList(list):
+ """Class for list of matches, since sometimes I need to add lines that aren't matches, I need an
+ independent counter."""
+ _sep = '...'
+ def __init__(self, *args):
+ list.__init__(self, *args)
+ self.matches_count = 0
+ self.stripped_lines = 0
+
+ def append(self, item):
+ """Append lines, can be a string or a list with strings."""
+ if isinstance(item, str):
+ list.append(self, item)
+ else:
+ self.extend(item)
+
+ def append_separator(self):
+ """adds a separator into the list, makes sure it doen't add two together."""
+ s = self._sep
+ if (self and self[-1] != s) or not self:
+ self.append(s)
+
+ def count_match(self, item=None):
+ if item is None or isinstance(item, str):
+ self.matches_count += 1
+ else:
+ self.matches_count += len(item)
+
+ def strip_separator(self):
+ """removes separators if there are first or/and last in the list."""
+ if self:
+ s = self._sep
+ if self[0] == s:
+ del self[0]
+ if self[-1] == s:
+ del self[-1]
+
+### Misc functions ###
+now = time.time
+def get_size(f):
+ try:
+ return stat(f).st_size
+ except OSError:
+ return 0
+
+sizeDict = {0:'b', 1:'KiB', 2:'MiB', 3:'GiB', 4:'TiB'}
+def human_readable_size(size):
+ power = 0
+ while size > 1024:
+ power += 1
+ size /= 1024.0
+ return '%.2f %s' %(size, sizeDict.get(power, ''))
+
+def color_nick(nick):
+ """Returns coloured nick, with coloured mode if any."""
+ if not nick: return ''
+ wcolor = weechat.color
+ config_string = lambda s : weechat.config_string(weechat.config_get(s))
+ config_int = lambda s : weechat.config_integer(weechat.config_get(s))
+ # prefix and suffix
+ prefix = config_string('irc.look.nick_prefix')
+ suffix = config_string('irc.look.nick_suffix')
+ prefix_c = suffix_c = wcolor(config_string('weechat.color.chat_delimiters'))
+ if nick[0] == prefix:
+ nick = nick[1:]
+ else:
+ prefix = prefix_c = ''
+ if nick[-1] == suffix:
+ nick = nick[:-1]
+ suffix = wcolor(color_delimiter) + suffix
+ else:
+ suffix = suffix_c = ''
+ # nick mode
+ modes = '@!+%'
+ if nick[0] in modes:
+ mode, nick = nick[0], nick[1:]
+ mode_color = wcolor(config_string('weechat.color.nicklist_prefix%d' \
+ %(modes.find(mode) + 1)))
+ else:
+ mode = mode_color = ''
+ # nick color
+ nick_color = weechat.info_get('irc_nick_color', nick)
+ if not nick_color:
+ # probably we're in WeeChat 0.3.0
+ #debug('no irc_nick_color')
+ color_nicks_number = config_int('weechat.look.color_nicks_number')
+ idx = (sum(map(ord, nick))%color_nicks_number) + 1
+ nick_color = wcolor(config_string('weechat.color.chat_nick_color%02d' %idx))
+ return ''.join((prefix_c, prefix, mode_color, mode, nick_color, nick, suffix_c, suffix))
+
+### Config and value validation ###
+boolDict = {'on':True, 'off':False}
+def get_config_boolean(config):
+ value = weechat.config_get_plugin(config)
+ try:
+ return boolDict[value]
+ except KeyError:
+ default = settings[config]
+ error("Error while fetching config '%s'. Using default value '%s'." %(config, default))
+ error("'%s' is invalid, allowed: 'on', 'off'" %value)
+ return boolDict[default]
+
+def get_config_int(config, allow_empty_string=False):
+ value = weechat.config_get_plugin(config)
+ try:
+ return int(value)
+ except ValueError:
+ if value == '' and allow_empty_string:
+ return value
+ default = settings[config]
+ error("Error while fetching config '%s'. Using default value '%s'." %(config, default))
+ error("'%s' is not a number." %value)
+ return int(default)
+
+def get_config_log_filter():
+ filter = weechat.config_get_plugin('log_filter')
+ if filter:
+ return filter.split(',')
+ else:
+ return []
+
+def get_home():
+ home = weechat.config_string(weechat.config_get('logger.file.path'))
+ return home.replace('%h', weechat.info_get('weechat_dir', ''))
+
+def strip_home(s, dir=''):
+ """Strips home dir from the begging of the log path, this makes them sorter."""
+ if not dir:
+ global home_dir
+ dir = home_dir
+ l = len(dir)
+ if s[:l] == dir:
+ return s[l:]
+ return s
+
+### Messages ###
+def debug(s, prefix='', buffer_name=None):
+ """Debug msg"""
+ if not weechat.config_get_plugin('debug'): return
+ if not buffer_name:
+ buffer_name = SCRIPT_NAME + '_debug'
+ buffer = weechat.buffer_search('python', buffer_name)
+ if not buffer:
+ buffer = weechat.buffer_new(buffer_name, '', '', '', '')
+ weechat.buffer_set(buffer, 'nicklist', '0')
+ weechat.buffer_set(buffer, 'localvar_set_no_log', '1')
+ weechat.prnt(buffer, '%s\t%s' %(prefix, s))
+
+def error(s, prefix=None, buffer='', trace=''):
+ """Error msg"""
+ prefix = prefix or script_nick
+ weechat.prnt(buffer, '%s%s %s' %(weechat.prefix('error'), prefix, s))
+ if weechat.config_get_plugin('debug'):
+ if not trace:
+ import traceback
+ if traceback.sys.exc_type:
+ trace = traceback.format_exc()
+ not trace or weechat.prnt('', trace)
+
+def say(s, prefix=None, buffer=''):
+ """normal msg"""
+ prefix = prefix or script_nick
+ weechat.prnt(buffer, '%s\t%s' %(prefix, s))
+
+### Log files and buffers ###
+cache_dir = {} # note: don't remove, needed for completion if the script was loaded recently
+def dir_list(dir, filter_list=(), filter_excludes=True, include_dir=False):
+ """Returns a list of files in 'dir' and its subdirs."""
+ global cache_dir
+ from os import walk
+ from fnmatch import fnmatch
+ #debug('dir_list: listing in %s' %dir)
+ key = (dir, include_dir)
+ try:
+ return cache_dir[key]
+ except KeyError:
+ pass
+
+ filter_list = filter_list or get_config_log_filter()
+ dir_len = len(dir)
+ if filter_list:
+ def filter(file):
+ file = file[dir_len:] # pattern shouldn't match home dir
+ for pattern in filter_list:
+ if fnmatch(file, pattern):
+ return filter_excludes
+ return not filter_excludes
+ else:
+ filter = lambda f : not filter_excludes
+
+ file_list = []
+ extend = file_list.extend
+ join = path.join
+
+ def walk_path():
+ for basedir, subdirs, files in walk(os.path.expanduser(dir)):
+ #if include_dir:
+ # subdirs = map(lambda s : join(s, ''), subdirs)
+ # files.extend(subdirs)
+ files_path = map(lambda f : join(basedir, f), files)
+ files_path = [ file for file in files_path if not filter(file) ]
+ extend(files_path)
+
+ walk_path()
+ cache_dir[key] = file_list
+ #debug('dir_list: got %s' %str(file_list))
+ return file_list
+
+def get_file_by_pattern(pattern, all=False):
+ """Returns the first log whose path matches 'pattern',
+ if all is True returns all logs that matches."""
+ if not pattern: return []
+ #debug('get_file_by_filename: searching for %s.' %pattern)
+ # do envvar expandsion and check file
+ file = path.expanduser(pattern)
+ file = path.expandvars(file)
+ if path.isfile(file):
+ return [file]
+ # lets see if there's a matching log
+ global home_dir
+ file = path.join(home_dir, pattern)
+ if path.isfile(file):
+ return [file]
+ else:
+ import fnmatch
+ file = []
+ file_list = dir_list(home_dir)
+ n = len(home_dir)
+ for log in file_list:
+ basename = log[n:]
+ if fnmatch.fnmatch(basename, pattern):
+ file.append(log)
+ if not all: break
+ #debug('get_file_by_filename: got %s.' %file)
+ return file
+
+def get_file_by_buffer(buffer):
+ """Given buffer pointer, finds log's path or returns None."""
+ #debug('get_file_by_buffer: searching for %s' %buffer)
+ infolist = weechat.infolist_get('logger_buffer', '', '')
+ if not infolist: return
+ try:
+ while weechat.infolist_next(infolist):
+ pointer = weechat.infolist_pointer(infolist, 'buffer')
+ if pointer == buffer:
+ file = weechat.infolist_string(infolist, 'log_filename')
+ if weechat.infolist_integer(infolist, 'log_enabled'):
+ #debug('get_file_by_buffer: got %s' %file)
+ return file
+ #else:
+ # debug('get_file_by_buffer: got %s but log not enabled' %file)
+ finally:
+ #debug('infolist gets freed')
+ weechat.infolist_free(infolist)
+
+def get_file_by_name(buffer_name):
+ """Given a buffer name, returns its log path or None. buffer_name should be in 'server.#channel'
+ or '#channel' format."""
+ #debug('get_file_by_name: searching for %s' %buffer_name)
+ # common mask options
+ config_masks = ('logger.mask.irc', 'logger.file.mask')
+ # since there's no buffer pointer, we try to replace some local vars in mask, like $channel and
+ # $server, then replace the local vars left with '*', and use it as a mask for get the path with
+ # get_file_by_pattern
+ for config in config_masks:
+ mask = weechat.config_string(weechat.config_get(config))
+ #debug('get_file_by_name: mask: %s' %mask)
+ if '$name' in mask:
+ mask = mask.replace('$name', buffer_name)
+ elif '$channel' in mask or '$server' in mask:
+ if '.' in buffer_name and \
+ '#' not in buffer_name[:buffer_name.find('.')]: # the dot isn't part of the channel name
+ # ^ I'm asuming channel starts with #, i'm lazy.
+ server, channel = buffer_name.split('.', 1)
+ else:
+ server, channel = '*', buffer_name
+ if '$channel' in mask:
+ mask = mask.replace('$channel', channel)
+ if '$server' in mask:
+ mask = mask.replace('$server', server)
+ # change the unreplaced vars by '*'
+ if '$' in mask:
+ chars = 'abcdefghijklmnopqrstuvwxyz_'
+ masks = mask.split('$')
+ masks = map(lambda s: s.lstrip(chars), masks)
+ mask = '*'.join(masks)
+ if mask[0] != '*':
+ mask = '*' + mask
+ #debug('get_file_by_name: using mask %s' %mask)
+ file = get_file_by_pattern(mask)
+ #debug('get_file_by_name: got file %s' %file)
+ if file:
+ return file
+ return None
+
+def get_buffer_by_name(buffer_name):
+ """Given a buffer name returns its buffer pointer or None."""
+ #debug('get_buffer_by_name: searching for %s' %buffer_name)
+ pointer = weechat.buffer_search('', buffer_name)
+ if not pointer:
+ try:
+ infolist = weechat.infolist_get('buffer', '', '')
+ while weechat.infolist_next(infolist):
+ short_name = weechat.infolist_string(infolist, 'short_name')
+ name = weechat.infolist_string(infolist, 'name')
+ if buffer_name in (short_name, name):
+ #debug('get_buffer_by_name: found %s' %name)
+ pointer = weechat.buffer_search('', name)
+ return pointer
+ finally:
+ weechat.infolist_free(infolist)
+ #debug('get_buffer_by_name: got %s' %pointer)
+ return pointer
+
+def get_all_buffers():
+ """Returns list with pointers of all open buffers."""
+ buffers = []
+ infolist = weechat.infolist_get('buffer', '', '')
+ while weechat.infolist_next(infolist):
+ buffers.append(weechat.infolist_pointer(infolist, 'pointer'))
+ weechat.infolist_free(infolist)
+ grep_buffer = weechat.buffer_search('python', SCRIPT_NAME)
+ if grep_buffer and grep_buffer in buffers:
+ # remove it from list
+ del buffers[buffers.index(grep_buffer)]
+ return buffers
+
+### Grep ###
+def make_regexp(pattern, matchcase=False):
+ """Returns a compiled regexp."""
+ if pattern in ('.', '.*', '.?', '.+'):
+ # because I don't need to use a regexp if we're going to match all lines
+ return None
+ try:
+ import re
+ if not matchcase:
+ regexp = re.compile(pattern, re.IGNORECASE)
+ else:
+ regexp = re.compile(pattern)
+ except Exception, e:
+ raise Exception, 'Bad pattern, %s' %e
+ return regexp
+
+def check_string(s, regexp, hilight='', exact=False):
+ """Checks 's' with a regexp and returns it if is a match."""
+ if not regexp:
+ return s
+ elif exact:
+ matchlist = regexp.findall(s)
+ if matchlist:
+ return matchlist
+ elif hilight:
+ matchlist = regexp.findall(s)
+ if matchlist:
+ matchlist = list(set(matchlist)) # remove duplicates if any
+ # apply hilight
+ color_hilight, color_reset = hilight.split(',', 1)
+ for m in matchlist:
+ s = s.replace(m, '%s%s%s' %(color_hilight, m, color_reset))
+ return s
+ # no need for findall() here
+ elif regexp.search(s):
+ return s
+
+def grep_file(file, head, tail, after_context, before_context, count, regexp, hilight, exact, invert):
+ """Return a list of lines that match 'regexp' in 'file', if no regexp returns all lines."""
+ if count:
+ tail = head = after_context = before_context = False
+ hilight = ''
+ elif exact:
+ before_context = after_context = False
+ hilight = ''
+ elif invert:
+ hilight = ''
+ #debug(' '.join(map(str, (file, head, tail, after_context, before_context))))
+
+ lines = linesList()
+ # define these locally as it makes the loop run slightly faster
+ append = lines.append
+ count_match = lines.count_match
+ separator = lines.append_separator
+ if invert:
+ def check(s):
+ if check_string(s, regexp, hilight, exact):
+ return None
+ else:
+ return s
+ else:
+ check = lambda s: check_string(s, regexp, hilight, exact)
+
+ try:
+ file_object = open(file, 'r')
+ except IOError:
+ # file doesn't exist
+ return lines
+ if tail or before_context:
+ # for these options, I need to seek in the file, but is slower and uses a good deal of
+ # memory if the log is too big, so we do this *only* for these options.
+ file_lines = file_object.readlines()
+
+ if before_context:
+ before_context_range = range(1, before_context + 1)
+ before_context_range.reverse()
+
+ if tail:
+ # instead of searching in the whole file and later pick the last few lines, we
+ # reverse the log, search until count reached and reverse it again, that way is a lot
+ # faster
+ file_lines.reverse()
+ limit = tail or head
+
+ line_idx = 0
+ while line_idx < len(file_lines):
+ line = file_lines[line_idx]
+ line = check(line)
+ if line:
+ if before_context:
+ separator()
+ trimmed = False
+ for id in before_context_range:
+ try:
+ context_line = file_lines[line_idx - id]
+ if check(context_line):
+ # match in before context, that means we appended these same lines in a
+ # previous match, so we delete them merging both paragraphs
+ if not trimmed:
+ del lines[id - before_context - 1:]
+ trimmed = True
+ else:
+ append(context_line)
+ except IndexError:
+ pass
+ append(line)
+ count_match(line)
+ if after_context:
+ id, offset = 0, 0
+ while id < after_context + offset:
+ id += 1
+ try:
+ context_line = file_lines[line_idx + id]
+ _context_line = check(context_line)
+ if _context_line:
+ offset = id
+ context_line = _context_line # so match is hilighted with --hilight
+ count_match()
+ append(context_line)
+ except IndexError:
+ pass
+ separator()
+ line_idx += id
+ if limit and lines.matches_count >= limit:
+ break
+ line_idx += 1
+
+ if tail:
+ lines.reverse()
+ else:
+ # do a normal grep
+ limit = head
+
+ for line in file_object:
+ line = check(line)
+ if line:
+ count or append(line)
+ count_match(line)
+ if after_context:
+ id, offset = 0, 0
+ while id < after_context + offset:
+ id += 1
+ try:
+ context_line = file_object.next()
+ _context_line = check(context_line)
+ if _context_line:
+ offset = id
+ context_line = _context_line
+ count_match()
+ count or append(context_line)
+ except StopIteration:
+ pass
+ separator()
+ if limit and lines.matches_count >= limit:
+ break
+
+ file_object.close()
+ return lines
+
+def grep_buffer(buffer, head, tail, after_context, before_context, count, regexp, hilight, exact,
+ invert):
+ """Return a list of lines that match 'regexp' in 'buffer', if no regexp returns all lines."""
+ lines = linesList()
+ if count:
+ tail = head = after_context = before_context = False
+ hilight = ''
+ elif exact:
+ before_context = after_context = False
+ #debug(' '.join(map(str, (tail, head, after_context, before_context, count, exact, hilight))))
+
+ # Using /grep in grep's buffer can lead to some funny effects
+ # We should take measures if that's the case
+ def make_get_line_funcion():
+ """Returns a function for get lines from the infolist, depending if the buffer is grep's or
+ not."""
+ string_remove_color = weechat.string_remove_color
+ infolist_string = weechat.infolist_string
+ grep_buffer = weechat.buffer_search('python', SCRIPT_NAME)
+ if grep_buffer and buffer == grep_buffer:
+ def function(infolist):
+ prefix = infolist_string(infolist, 'prefix')
+ message = infolist_string(infolist, 'message')
+ if prefix: # only our messages have prefix, ignore it
+ return None
+ return message
+ else:
+ infolist_time = weechat.infolist_time
+ def function(infolist):
+ prefix = string_remove_color(infolist_string(infolist, 'prefix'), '')
+ message = string_remove_color(infolist_string(infolist, 'message'), '')
+ date = infolist_time(infolist, 'date')
+ return '%s\t%s\t%s' %(date, prefix, message)
+ return function
+ get_line = make_get_line_funcion()
+
+ infolist = weechat.infolist_get('buffer_lines', buffer, '')
+ if tail:
+ # like with grep_file() if we need the last few matching lines, we move the cursor to
+ # the end and search backwards
+ infolist_next = weechat.infolist_prev
+ infolist_prev = weechat.infolist_next
+ else:
+ infolist_next = weechat.infolist_next
+ infolist_prev = weechat.infolist_prev
+ limit = head or tail
+
+ # define these locally as it makes the loop run slightly faster
+ append = lines.append
+ count_match = lines.count_match
+ separator = lines.append_separator
+ if invert:
+ def check(s):
+ if check_string(s, regexp, hilight, exact):
+ return None
+ else:
+ return s
+ else:
+ check = lambda s: check_string(s, regexp, hilight, exact)
+
+ if before_context:
+ before_context_range = range(1, before_context + 1)
+ before_context_range.reverse()
+
+ while infolist_next(infolist):
+ line = get_line(infolist)
+ if line is None: continue
+ line = check(line)
+ if line:
+ if before_context:
+ separator()
+ trimmed = False
+ for id in before_context_range:
+ if not infolist_prev(infolist):
+ trimmed = True
+ for id in before_context_range:
+ context_line = get_line(infolist)
+ if check(context_line):
+ if not trimmed:
+ del lines[id - before_context - 1:]
+ trimmed = True
+ else:
+ append(context_line)
+ infolist_next(infolist)
+ count or append(line)
+ count_match(line)
+ if after_context:
+ id, offset = 0, 0
+ while id < after_context + offset:
+ id += 1
+ if infolist_next(infolist):
+ context_line = get_line(infolist)
+ _context_line = check(context_line)
+ if _context_line:
+ context_line = _context_line
+ offset = id
+ count_match()
+ append(context_line)
+ else:
+ # in the main loop infolist_next will start again an cause an infinite loop
+ # this will avoid it
+ infolist_next = lambda x: 0
+ separator()
+ if limit and lines.matches_count >= limit:
+ break
+ weechat.infolist_free(infolist)
+
+ if tail:
+ lines.reverse()
+ return lines
+
+### this is our main grep function
+hook_file_grep = None
+def show_matching_lines():
+ """
+ Greps buffers in search_in_buffers or files in search_in_files and updates grep buffer with the
+ result.
+ """
+ global pattern, matchcase, number, count, exact, hilight, invert
+ global tail, head, after_context, before_context
+ global search_in_files, search_in_buffers, matched_lines, home_dir
+ global time_start
+ matched_lines = linesDict()
+ #debug('buffers:%s \nlogs:%s' %(search_in_buffers, search_in_files))
+ time_start = now()
+
+ # buffers
+ if search_in_buffers:
+ regexp = make_regexp(pattern, matchcase)
+ for buffer in search_in_buffers:
+ buffer_name = weechat.buffer_get_string(buffer, 'name')
+ matched_lines[buffer_name] = grep_buffer(buffer, head, tail, after_context,
+ before_context, count, regexp, hilight, exact, invert)
+
+ # logs
+ if search_in_files:
+ size_limit = get_config_int('size_limit', allow_empty_string=True)
+ background = False
+ if size_limit or size_limit == 0:
+ size = sum(map(get_size, search_in_files))
+ if size > size_limit * 1024:
+ background = True
+ elif size_limit == '':
+ background = False
+
+ if not background:
+ # run grep normally
+ regexp = make_regexp(pattern, matchcase)
+ for log in search_in_files:
+ log_name = strip_home(log)
+ matched_lines[log_name] = grep_file(log, head, tail, after_context, before_context,
+ count, regexp, hilight, exact, invert)
+ buffer_update()
+ else:
+ # we hook a process so grepping runs in background.
+ #debug('on background')
+ global hook_file_grep, script_path, bytecode
+ timeout = 1000*60*10 # 10 min
+
+ quotify = lambda s: '"%s"' %s
+ files_string = ', '.join(map(quotify, search_in_files))
+
+ cmd = grep_proccess_cmd %dict(logs=files_string, head=head, pattern=pattern, tail=tail,
+ hilight=hilight, after_context=after_context, before_context=before_context,
+ exact=exact, matchcase=matchcase, home_dir=home_dir, script_path=script_path,
+ count=count, invert=invert, bytecode=bytecode)
+
+ #debug(cmd)
+ hook_file_grep = weechat.hook_process(cmd, timeout, 'grep_file_callback', '')
+ if hook_file_grep:
+ buffer_create("Searching for '%s' in %s worth of data..." %(pattern,
+ human_readable_size(size)))
+ else:
+ buffer_update()
+
+# defined here for commodity
+grep_proccess_cmd = """python -%(bytecode)sc '
+import sys, cPickle
+sys.path.append("%(script_path)s") # add WeeChat script dir so we can import grep
+from grep import make_regexp, grep_file, strip_home
+logs = (%(logs)s, )
+try:
+ regexp = make_regexp("%(pattern)s", %(matchcase)s)
+ d = {}
+ for log in logs:
+ log_name = strip_home(log, "%(home_dir)s")
+ lines = grep_file(log, %(head)s, %(tail)s, %(after_context)s, %(before_context)s,
+ %(count)s, regexp, "%(hilight)s", %(exact)s, %(invert)s)
+ d[log_name] = lines
+ fdname = "/tmp/grep_search.tmp"
+ fd = open(fdname, "wb")
+ print fdname
+ cPickle.dump(d, fd, -1)
+ fd.close()
+except Exception, e:
+ print >> sys.stderr, e'
+"""
+
+grep_stdout = grep_stderr = ''
+def grep_file_callback(data, command, rc, stdout, stderr):
+ global hook_file_grep, grep_stderr, grep_stdout
+ global matched_lines
+ #debug("rc: %s\nstderr: %s\nstdout: %s" %(rc, repr(stderr), repr(stdout)))
+ if stdout:
+ grep_stdout += stdout
+ if stderr:
+ grep_stderr += stderr
+ if int(rc) >= 0:
+
+ def set_buffer_error():
+ grep_buffer = buffer_create()
+ title = weechat.buffer_get_string(grep_buffer, 'title')
+ title = title + ' %serror' %color_title
+ weechat.buffer_set(grep_buffer, 'title', title)
+
+ try:
+ if grep_stderr:
+ error(grep_stderr)
+ set_buffer_error()
+ elif grep_stdout:
+ #debug(grep_stdout)
+ file = grep_stdout.strip()
+ if file:
+ try:
+ import cPickle, os
+ #debug(file)
+ fd = open(file, 'rb')
+ d = cPickle.load(fd)
+ matched_lines.update(d)
+ fd.close()
+ except Exception, e:
+ error(e)
+ set_buffer_error()
+ else:
+ os.remove(file)
+ buffer_update()
+ finally:
+ grep_stdout = grep_stderr = ''
+ hook_file_grep = None
+ return WEECHAT_RC_OK
+
+def get_grep_file_status():
+ global search_in_files, matched_lines, time_start
+ elapsed = now() - time_start
+ if len(search_in_files) == 1:
+ log = '%s (%s)' %(strip_home(search_in_files[0]),
+ human_readable_size(get_size(search_in_files[0])))
+ else:
+ size = sum(map(get_size, search_in_files))
+ log = '%s log files (%s)' %(len(search_in_files), human_readable_size(size))
+ return 'Searching in %s, running for %.4f seconds. Interrupt it with "/grep stop" or "stop"' \
+ ' in grep buffer.' %(log, elapsed)
+
+### Grep buffer ###
+def buffer_update():
+ """Updates our buffer with new lines."""
+ global matched_lines, pattern, count, hilight, invert
+ time_grep = now()
+
+ buffer = buffer_create()
+ if get_config_boolean('clear_buffer'):
+ weechat.buffer_clear(buffer)
+ matched_lines.strip_separator() # remove first and last separators of each list
+ len_total_lines = len(matched_lines)
+ max_lines = get_config_int('max_lines')
+ if not count and len_total_lines > max_lines:
+ weechat.buffer_clear(buffer)
+
+ def _make_summary(log, lines, note):
+ return '%s matches "%s%s%s"%s in %s%s%s%s' \
+ %(lines.matches_count, color_summary, pattern, color_info,
+ invert and ' (inverted)' or '',
+ color_summary, log, color_reset, note)
+
+ if count:
+ make_summary = lambda log, lines : _make_summary(log, lines, ' (not shown)')
+ else:
+ def make_summary(log, lines):
+ if lines.stripped_lines:
+ if lines:
+ note = ' (last %s lines shown)' %len(lines)
+ else:
+ note = ' (not shown)'
+ else:
+ note = ''
+ return _make_summary(log, lines, note)
+
+ global weechat_format
+ if hilight:
+ # we don't want colors if there's match highlighting
+ format_line = lambda s : '%s %s %s' %split_line(s)
+ else:
+ def format_line(s):
+ global nick_dict, weechat_format
+ date, nick, msg = split_line(s)
+ if weechat_format:
+ try:
+ nick = nick_dict[nick]
+ except KeyError:
+ # cache nick
+ nick_c = color_nick(nick)
+ nick_dict[nick] = nick_c
+ nick = nick_c
+ return '%s%s %s%s %s' %(color_date, date, nick, color_reset, msg)
+ else:
+ #no formatting
+ return msg
+
+ prnt = weechat.prnt
+ prnt(buffer, '\n')
+ print_line('Search for "%s%s%s"%s in %s%s%s.' %(color_summary, pattern, color_info,
+ invert and ' (inverted)' or '', color_summary, matched_lines, color_reset),
+ buffer)
+ # print last <max_lines> lines
+ if matched_lines.get_matches_count():
+ if count:
+ # with count we sort by matches lines instead of just lines.
+ matched_lines_items = matched_lines.items_count()
+ else:
+ matched_lines_items = matched_lines.items()
+
+ matched_lines.get_last_lines(max_lines)
+ for log, lines in matched_lines_items:
+ if lines.matches_count:
+ # matched lines
+ if not count:
+ # print lines
+ weechat_format = True
+ for line in lines:
+ #debug(repr(line))
+ if line == linesList._sep:
+ # separator
+ prnt(buffer, context_sep)
+ else:
+ if '\x00' in line:
+ # log was corrupted
+ error("Found garbage in log '%s', maybe it's corrupted" %log,
+ trace=repr(line))
+ line = line.replace('\x00', '')
+ prnt(buffer, format_line(line))
+
+ # summary
+ if count or get_config_boolean('show_summary'):
+ summary = make_summary(log, lines)
+ print_line(summary, buffer)
+
+ # separator
+ if not count and lines:
+ prnt(buffer, '\n')
+ else:
+ print_line('No matches found.', buffer)
+
+ # set title
+ global time_start
+ time_end = now()
+ # total time
+ time_total = time_end - time_start
+ # percent of the total time used for grepping
+ time_grep_pct = (time_grep - time_start)/time_total*100
+ #debug('time: %.4f seconds (%.2f%%)' %(time_total, time_grep_pct))
+ if not count and len_total_lines > max_lines:
+ note = ' (last %s lines shown)' %len(matched_lines)
+ else:
+ note = ''
+ title = "Search in %s%s%s %s matches%s | pattern \"%s%s%s\"%s %s | %.4f seconds (%.2f%%)" \
+ %(color_title, matched_lines, color_reset, matched_lines.get_matches_count(), note,
+ color_title, pattern, color_reset, invert and ' (inverted)' or '', format_options(),
+ time_total, time_grep_pct)
+ weechat.buffer_set(buffer, 'title', title)
+
+ if get_config_boolean('go_to_buffer'):
+ weechat.buffer_set(buffer, 'display', '1')
+
+ # free matched_lines so it can be removed from memory
+ del matched_lines
+
+def split_line(s):
+ """Splits log's line 's' in 3 parts, date, nick and msg."""
+ global weechat_format
+ if weechat_format and s.count('\t') >= 2:
+ date, nick, msg = s.split('\t', 2) # date, nick, message
+ else:
+ # looks like log isn't in weechat's format
+ weechat_format = False # incoming lines won't be formatted
+ date, nick, msg = '', '', s
+ # remove tabs
+ if '\t' in msg:
+ msg = msg.replace('\t', ' ')
+ return date, nick, msg
+
+def print_line(s, buffer=None, display=False):
+ """Prints 's' in script's buffer as 'script_nick'. For displaying search summaries."""
+ if buffer is None:
+ buffer = buffer_create()
+ say('%s%s' %(color_info, s), buffer=buffer)
+ if display and get_config_boolean('go_to_buffer'):
+ weechat.buffer_set(buffer, 'display', '1')
+
+def format_options():
+ global matchcase, number, count, exact, hilight, invert
+ global tail, head, after_context, before_context
+ options = []
+ append = options.append
+ insert = options.insert
+ chars = 'cHmov'
+ for i, flag in enumerate((count, hilight, matchcase, exact, invert)):
+ if flag:
+ append(chars[i])
+
+ if head or tail:
+ n = get_config_int('default_tail_head')
+ if head:
+ append('h')
+ if head != n:
+ insert(-1, ' -')
+ append('n')
+ append(head)
+ elif tail:
+ append('t')
+ if tail != n:
+ insert(-1, ' -')
+ append('n')
+ append(tail)
+
+ if before_context and after_context and (before_context == after_context):
+ append(' -C')
+ append(before_context)
+ else:
+ if before_context:
+ append(' -B')
+ append(before_context)
+ if after_context:
+ append(' -A')
+ append(after_context)
+
+ s = ''.join(map(str, options)).strip()
+ if s and s[0] != '-':
+ s = '-' + s
+ return s
+
+def buffer_create(title=None):
+ """Returns our buffer pointer, creates and cleans the buffer if needed."""
+ buffer = weechat.buffer_search('python', SCRIPT_NAME)
+ if not buffer:
+ buffer = weechat.buffer_new(SCRIPT_NAME, 'buffer_input', '', '', '')
+ weechat.buffer_set(buffer, 'time_for_each_line', '0')
+ weechat.buffer_set(buffer, 'nicklist', '0')
+ weechat.buffer_set(buffer, 'title', title or 'grep output buffer')
+ weechat.buffer_set(buffer, 'localvar_set_no_log', '1')
+ elif title:
+ weechat.buffer_set(buffer, 'title', title)
+ return buffer
+
+def buffer_input(data, buffer, input_data):
+ """Repeats last search with 'input_data' as regexp."""
+ try:
+ cmd_grep_stop(buffer, input_data)
+ except:
+ return WEECHAT_RC_OK
+
+ global search_in_buffers, search_in_files
+ global pattern
+ try:
+ if pattern and (search_in_files or search_in_buffers):
+ # check if the buffer pointers are still valid
+ for pointer in search_in_buffers:
+ infolist = weechat.infolist_get('buffer', pointer, '')
+ if not infolist:
+ del search_in_buffers[search_in_buffers.index(pointer)]
+ weechat.infolist_free(infolist)
+ try:
+ cmd_grep_parsing(input_data)
+ except Exception, e:
+ error('Argument error, %s' %e, buffer=buffer)
+ return WEECHAT_RC_OK
+ try:
+ show_matching_lines()
+ except Exception, e:
+ error(e)
+ except NameError:
+ error("There isn't any previous search to repeat.", buffer=buffer)
+ return WEECHAT_RC_OK
+
+### Commands ###
+def cmd_init():
+ """Resets global vars."""
+ global home_dir, cache_dir, nick_dict
+ global pattern, matchcase, number, count, exact, hilight, invert
+ global tail, head, after_context, before_context
+ hilight = ''
+ head = tail = after_context = before_context = invert = False
+ matchcase = count = exact = False
+ pattern = number = None
+ home_dir = get_home()
+ cache_dir = {} # for avoid walking the dir tree more than once per command
+ nick_dict = {} # nick cache for don't calculate nick color every time
+
+def cmd_grep_parsing(args):
+ """Parses args for /grep and grep input buffer."""
+ global pattern, matchcase, number, count, exact, hilight, invert
+ global tail, head, after_context, before_context
+ global log_name, buffer_name, only_buffers, all
+ opts, args = getopt.gnu_getopt(args.split(), 'cmHeahtivn:bA:B:C:o', ['count', 'matchcase', 'hilight',
+ 'exact', 'all', 'head', 'tail', 'number=', 'buffer', 'after-context=', 'before-context=',
+ 'context=', 'invert', 'only-match'])
+ #debug(opts, 'opts: '); debug(args, 'args: ')
+ if len(args) >= 2:
+ if args[0] == 'log':
+ del args[0]
+ log_name = args.pop(0)
+ elif args[0] == 'buffer':
+ del args[0]
+ buffer_name = args.pop(0)
+ args = ' '.join(args) # join pattern for keep spaces
+ if args:
+ pattern = args
+ elif not pattern:
+ raise Exception, 'No pattern for grep the logs.'
+
+ def positive_number(opt, val):
+ try:
+ number = int(val)
+ if number < 0:
+ raise ValueError
+ return number
+ except ValueError:
+ if len(opt) == 1:
+ opt = '-' + opt
+ else:
+ opt = '--' + opt
+ raise Exception, "argument for %s must be a positive integer." %opt
+
+ for opt, val in opts:
+ opt = opt.strip('-')
+ if opt in ('c', 'count'):
+ count = not count
+ elif opt in ('m', 'matchcase'):
+ matchcase = not matchcase
+ elif opt in ('H', 'hilight'):
+ # hilight must be always a string!
+ if hilight:
+ hilight = ''
+ else:
+ hilight = '%s,%s' %(color_hilight, color_reset)
+ # we pass the colors in the variable itself because check_string() must not use
+ # weechat's module when applying the colors (this is for grep in a hooked process)
+ elif opt in ('e', 'exact', 'o', 'only-match'):
+ exact = not exact
+ invert = False
+ elif opt in ('a', 'all'):
+ all = not all
+ elif opt in ('h', 'head'):
+ head = not head
+ tail = False
+ elif opt in ('t', 'tail'):
+ tail = not tail
+ head = False
+ elif opt in ('b', 'buffer'):
+ only_buffers = True
+ elif opt in ('n', 'number'):
+ number = positive_number(opt, val)
+ elif opt in ('C', 'context'):
+ n = positive_number(opt, val)
+ after_context = n
+ before_context = n
+ elif opt in ('A', 'after-context'):
+ after_context = positive_number(opt, val)
+ elif opt in ('B', 'before-context'):
+ before_context = positive_number(opt, val)
+ elif opt in ('i', 'v', 'invert'):
+ invert = not invert
+ exact = False
+ # number check
+ if number is not None:
+ if number == 0:
+ head = tail = False
+ number = None
+ elif head:
+ head = number
+ elif tail:
+ tail = number
+ else:
+ n = get_config_int('default_tail_head')
+ if head:
+ head = n
+ elif tail:
+ tail = n
+
+def cmd_grep_stop(buffer, args):
+ global hook_file_grep, pattern, matched_lines
+ if hook_file_grep:
+ if args == 'stop':
+ weechat.unhook(hook_file_grep)
+ hook_file_grep = None
+ s = 'Search for \'%s\' stopped.' %pattern
+ say(s, buffer=buffer)
+ grep_buffer = weechat.buffer_search('python', SCRIPT_NAME)
+ if grep_buffer:
+ weechat.buffer_set(grep_buffer, 'title', s)
+ del matched_lines
+ else:
+ say(get_grep_file_status(), buffer=buffer)
+ raise Exception
+
+def cmd_grep(data, buffer, args):
+ """Search in buffers and logs."""
+ global pattern, matchcase, head, tail, number, count, exact, hilight
+ try:
+ cmd_grep_stop(buffer, args)
+ except:
+ return WEECHAT_RC_OK
+
+ if not args:
+ weechat.command('', '/help %s' %SCRIPT_COMMAND)
+ return WEECHAT_RC_OK
+
+ cmd_init()
+ global log_name, buffer_name, only_buffers, all
+ log_name = buffer_name = ''
+ only_buffers = all = False
+
+ # parse
+ try:
+ cmd_grep_parsing(args)
+ except Exception, e:
+ error('Argument error, %s' %e)
+ return WEECHAT_RC_OK
+
+ # find logs
+ log_file = search_buffer = None
+ if log_name:
+ log_file = get_file_by_pattern(log_name, all)
+ if not log_file:
+ error("Couldn't find any log for %s. Try /logs" %log_name)
+ return WEECHAT_RC_OK
+ elif all:
+ search_buffer = get_all_buffers()
+ elif buffer_name:
+ search_buffer = get_buffer_by_name(buffer_name)
+ if not search_buffer:
+ # there's no buffer, try in the logs
+ log_file = get_file_by_name(buffer_name)
+ if not log_file:
+ error("Logs or buffer for '%s' not found." %buffer_name)
+ return WEECHAT_RC_OK
+ else:
+ search_buffer = [search_buffer]
+ else:
+ search_buffer = [buffer]
+
+ # make the log list
+ global search_in_files, search_in_buffers
+ search_in_files = []
+ search_in_buffers = []
+ if log_file:
+ search_in_files = log_file
+ elif not only_buffers:
+ #debug(search_buffer)
+ for pointer in search_buffer:
+ log = get_file_by_buffer(pointer)
+ #debug('buffer %s log %s' %(pointer, log))
+ if log:
+ search_in_files.append(log)
+ else:
+ search_in_buffers.append(pointer)
+ else:
+ search_in_buffers = search_buffer
+
+ # grepping
+ try:
+ show_matching_lines()
+ except Exception, e:
+ error(e)
+ return WEECHAT_RC_OK
+
+def cmd_logs(data, buffer, args):
+ """List files in Weechat's log dir."""
+ cmd_init()
+ global home_dir
+ sort_by_size = False
+ filter = []
+
+ try:
+ opts, args = getopt.gnu_getopt(args.split(), 's', ['size'])
+ if args:
+ filter = args
+ for opt, var in opts:
+ opt = opt.strip('-')
+ if opt in ('size', 's'):
+ sort_by_size = True
+ except Exception, e:
+ error('Argument error, %s' %e)
+ return WEECHAT_RC_OK
+
+ # is there's a filter, filter_excludes should be False
+ file_list = dir_list(home_dir, filter, filter_excludes=not filter)
+ if sort_by_size:
+ file_list.sort(key=get_size)
+ else:
+ file_list.sort()
+
+ file_sizes = map(lambda x: human_readable_size(get_size(x)), file_list)
+ # calculate column lenght
+ if file_list:
+ L = file_list[:]
+ L.sort(key=len)
+ bigest = L[-1]
+ column_len = len(bigest) + 3
+ else:
+ column_len = ''
+
+ buffer = buffer_create()
+ if get_config_boolean('clear_buffer'):
+ weechat.buffer_clear(buffer)
+ file_list = zip(file_list, file_sizes)
+ msg = 'Found %s logs.' %len(file_list)
+
+ print_line(msg, buffer, display=True)
+ for file, size in file_list:
+ separator = column_len and '.'*(column_len - len(file))
+ weechat.prnt(buffer, '%s %s %s' %(strip_home(file), separator, size))
+ if file_list:
+ print_line(msg, buffer)
+ return WEECHAT_RC_OK
+
+### Completion ###
+def completion_log_files(data, completion_item, buffer, completion):
+ #debug('completion: %s' %', '.join((data, completion_item, buffer, completion)))
+ global home_dir
+ l = len(home_dir)
+ completion_list_add = weechat.hook_completion_list_add
+ WEECHAT_LIST_POS_END = weechat.WEECHAT_LIST_POS_END
+ for log in dir_list(home_dir):
+ completion_list_add(completion, log[l:], 0, WEECHAT_LIST_POS_END)
+ return WEECHAT_RC_OK
+
+def completion_grep_args(data, completion_item, buffer, completion):
+ for arg in ('count', 'all', 'matchcase', 'hilight', 'exact', 'head', 'tail', 'number', 'buffer',
+ 'after-context', 'before-context', 'context', 'invert', 'only-match'):
+ weechat.hook_completion_list_add(completion, '--' + arg, 0, weechat.WEECHAT_LIST_POS_SORT)
+ return WEECHAT_RC_OK
+
+### Main ###
+def delete_bytecode():
+ global script_path
+ bytecode = path.join(script_path, SCRIPT_NAME + '.pyc')
+ if path.isfile(bytecode):
+ os.remove(bytecode)
+ return WEECHAT_RC_OK
+
+if __name__ == '__main__' and import_ok and \
+ weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION, SCRIPT_LICENSE, \
+ SCRIPT_DESC, 'delete_bytecode', ''):
+ home_dir = get_home()
+
+ # for import ourselves
+ global script_path
+ script_path = path.dirname(__file__)
+ sys.path.append(script_path)
+ delete_bytecode()
+
+ # check python version
+ import sys
+ global bytecode
+ if sys.version_info > (2, 6):
+ bytecode = 'B'
+ else:
+ bytecode = ''
+
+
+ weechat.hook_command(SCRIPT_COMMAND, cmd_grep.__doc__,
+ "[log <file> | buffer <name> | stop] [-a|--all] [-b|--buffer] [-c|--count] [-m|--matchcase] "
+ "[-H|--hilight] [-o|--only-match] [-i|-v|--invert] [(-h|--head)|(-t|--tail) [-n|--number <n>]] "
+ "[-A|--after-context <n>] [-B|--before-context <n>] [-C|--context <n> ] <expression>",
+# help
+"""
+ log <file>: Search in one log that matches <file> in the logger path.
+ Use '*' and '?' as wildcards.
+ buffer <name>: Search in buffer <name>, if there's no buffer with <name> it will
+ try to search for a log file.
+ stop: Stops a currently running search.
+ -a --all: Search in all open buffers.
+ If used with 'log <file>' search in all logs that matches <file>.
+ -b --buffer: Search only in buffers, not in file logs.
+ -c --count: Just count the number of matched lines instead of showing them.
+ -m --matchcase: Don't do case insensible search.
+ -H --hilight: Colour exact matches in output buffer.
+-o --only-match: Print only the matching part of the line.
+ -v -i --invert: Print lines that don't match the regular expression.
+ -t --tail: Print the last 10 matching lines.
+ -h --head: Print the first 10 matching lines.
+-n --number <n>: Overrides default number of lines for --tail or --head.
+-A --after-context <n>: Shows <n> lines of trailing context after matching lines.
+-B --before-context <n>: Shows <n> lines of leading context before matching lines.
+-C --context <n>: Same as using both --after-context and --before-context simultaneously.
+ <expression>: Expression to search.
+
+Grep buffer:
+ Input line accepts most arguemnts of /grep, it'll repeat last search using the new
+ arguments provided. You can't search in different logs from the buffer's input.
+ Boolean arguments like --count, --tail, --head, --hilight, ... are toggleable
+
+Python regular expression syntax:
+ See http://docs.python.org/lib/re-syntax.html
+""",
+ # completion template
+ "buffer %(buffers_names) %(grep_arguments)|%*"
+ "||log %(grep_log_files) %(grep_arguments)|%*"
+ "||stop"
+ "||%(grep_arguments)|%*",
+ 'cmd_grep' ,'')
+ weechat.hook_command('logs', cmd_logs.__doc__, "[-s|--size] [<filter>]",
+ "-s --size: Sort logs by size.\n"
+ " <filter>: Only show logs that match <filter>. Use '*' and '?' as wildcards.", '--size', 'cmd_logs', '')
+
+ weechat.hook_completion('grep_log_files', "list of log files",
+ 'completion_log_files', '')
+ weechat.hook_completion('grep_arguments', "list of arguments",
+ 'completion_grep_args', '')
+
+ # settings
+ for opt, val in settings.iteritems():
+ if not weechat.config_is_set_plugin(opt):
+ weechat.config_set_plugin(opt, val)
+
+ # colors
+ color_date = weechat.color('brown')
+ color_info = weechat.color('cyan')
+ color_hilight = weechat.color('lightred')
+ color_reset = weechat.color('reset')
+ color_title = weechat.color('yellow')
+ color_summary = weechat.color('lightcyan')
+ color_delimiter = weechat.color('chat_delimiters')
+ color_script_nick = weechat.color('chat_nick')
+
+ # pretty [grep]
+ script_nick = '%s[%s%s%s]%s' %(color_delimiter, color_script_nick, SCRIPT_NAME, color_delimiter,
+ color_reset)
+ script_nick_nocolor = '[%s]' %SCRIPT_NAME
+ # paragraph separator when using context options
+ context_sep = '%s\t%s--' %(script_nick, color_info)
+
+# vim:set shiftwidth=4 tabstop=4 softtabstop=4 expandtab textwidth=100:
diff --git a/notify.py b/notify.py
new file mode 100644
index 0000000..c8b91e9
--- a/dev/null
+++ b/notify.py
@@ -0,0 +1,117 @@
+# Author: lavaramano <lavaramano AT gmail DOT com>
+# Improved by: BaSh - <bash.lnx AT gmail DOT com>
+# Ported to Weechat 0.3.0 by: Sharn - <sharntehnub AT gmail DOT com)
+# This Plugin Calls the libnotify bindings via python when somebody says your nickname, sends you a query, etc.
+# To make it work, you may need to download: python-notify (and libnotify - libgtk)
+# Requires Weechat 0.3.0
+# Released under GNU GPL v2
+#
+# 2010-09-22, Raghavendra Prabhu
+# Added some stuff.
+# 2010-02-20, Aron Griffis <[email protected]>
+# version 0.0.5: Add nick_separator, don't call show_notification twice on
+# privmsg, fix spelling s/nofify/notify/, use nick as "summary" for privmsg
+# notification, fit in 80 columns, tweak vim modeline.
+# 2010-01-24, David Rubin <[email protected]>
+# version 0.0.4.2 Fixed issue with self notifications when used with out "smart_notification"
+# 2010-01-19, Didier Roche <[email protected]>
+# version 0.0.4.1: add private message sender name
+# 2010-01-19, Didier Roche <[email protected]>
+# version 0.0.4: add smart notification:
+# be notified only if you're not in the current channel/pv window (off by default)
+# 2009-06-16, kba <[email protected]>:
+# version 0.0.3: added config options for icon and urgency
+# 2009-05-02, FlashCode <[email protected]>:
+# version 0.0.2.1: sync with last API changes
+
+import weechat, pynotify, string
+weechat.register("notify", "lavaramano", "0.0.5", "GPL", "notify: A real time notification system for weechat", "", "")
+
+# script options
+settings = {
+ "show_hilights" : "on",
+ "show_priv_msg" : "on",
+ "nick_separator" : ": ",
+ "icon" : "/usr/share/pixmaps/weechat.xpm",
+ "path" : "~/logs/me.log",
+ "urgency" : "normal",
+ "smart_notification" : "off",
+}
+
+urgencies = {
+ "low" : pynotify.URGENCY_LOW,
+ "critical" : pynotify.URGENCY_CRITICAL,
+ "normal" : pynotify.URGENCY_NORMAL,
+}
+
+# Init everything
+for option, default_value in settings.items():
+ if weechat.config_get_plugin(option) == "":
+ weechat.config_set_plugin(option, default_value)
+
+# Hook privmsg/hilights
+weechat.hook_print("", "irc_privmsg", "", 1, "notify_show", "")
+
+# Functions
+def notify_show(data, bufferp, uber_empty, tagsn, isdisplayed,
+ ishilight, prefix, message):
+ """Sends highlighted message to be printed on notification"""
+ try:
+ if (weechat.config_get_plugin('smart_notification') == "on" and
+ bufferp == weechat.current_buffer() and focussed()):
+ pass
+
+ elif (weechat.buffer_get_string(bufferp, "localvar_type") == "private" and
+ weechat.config_get_plugin('show_priv_msg') == "on"):
+ show_notification(prefix, message)
+
+ elif (ishilight == "1" and
+ weechat.config_get_plugin('show_hilights') == "on"):
+ buffer = (weechat.buffer_get_string(bufferp, "short_name") or
+ weechat.buffer_get_string(bufferp, "name"))
+ show_notification(buffer, prefix +
+ weechat.config_get_plugin('nick_separator') + message)
+ except ex:
+ writetofile(weechat.config_get_plugin('path'),prefix,message,str(ex))
+
+ return weechat.WEECHAT_RC_OK
+
+def show_notification(chan,message):
+ print('\a')
+ pynotify.init("wee-notifier")
+ writetofile(weechat.config_get_plugin('path'),chan,message)
+ wn = pynotify.Notification(chan, message, weechat.config_get_plugin('icon'))
+ wn.set_urgency(urgencies[weechat.config_get_plugin('urgency')] or
+ pynotify.URGENCY_NORMAL)
+ wn.show()
+
+def writetofile(path,chan,message,exception=""):
+ import os
+ import datetime
+ fullpath = os.path.expanduser(path)
+ file = open(fullpath,"a")
+ file.writelines(str(datetime.datetime.now().ctime()))
+ if exception:
+ file.writelines("\n\n --------> "+exception+"<------------\n")
+ file.writelines(" \n\nWeechat priv:==============>\n")
+ file.writelines(str(chan+" : "+message+"\n"))
+ file.writelines("<==============\n\n")
+ file.close()
+
+
+
+def focussed():
+ import subprocess
+ import re
+ # tmux display-message -p and xdotool getactivewindow getwindowpid
+ x,y=subprocess.Popen("\
+ [[ `xdotool getactivewindow getwindowpid` == `pidof urxvtd` ]];\
+ echo -n $?",shell=True,stdout=subprocess.PIPE).communicate()
+ if(x == 0):
+ current,discard = subprocess.Popen("tmux display-message -p",shell=True\
+ ,stdout=subprocess.PIPE).communicate()
+ if re.match(".*weechat.*",current):
+ return True
+ return False
+
+# vim: autoindent expandtab smarttab shiftwidth=4