igor@155: #!/usr/bin/perl -w
igor@155: 
igor@155: use POSIX qw(strftime);
igor@155: use lib '/etc/lilalo';
igor@155: use l3config;
igor@155: use utf8;
igor@155: use DBI;
igor@155: #no warnings;
igor@155: 
igor@155: our @Fields=qw/id time prompt cline output err last_command tab diff l3cd/;
igor@155: our $Fields=join(", ",@Fields);
igor@155: our $db;
igor@155: 
igor@155: =cut
igor@155: Множество команд, с которыми выполняется операция,
igor@155: определяется текущим контекстом и текущей строкой,
igor@155: а также параметрами:
igor@155:     * фильтр;
igor@155:     * регулярное выражение;
igor@155:     * интервал.
igor@155: =cut 
igor@155: our $Filter;
igor@155: our $Grep;
igor@155: our $Interval;
igor@155: our $Command;
igor@155: 
igor@155: # Если мы используем режим -f, то в этой переменной запоминается
igor@155: # до какой точки мы дошли
igor@155: our $Follow=0;
igor@155: 
igor@155: our $Context;
igor@155: 
igor@155: our @Commands = qw/head tail cat history/;
igor@155: 
igor@155: sub main();
igor@155: sub connect_database();
igor@155: 
igor@155: main();
igor@155: 
igor@155: sub main()
igor@155: {
igor@155:     $| = 1;
igor@155: 
igor@155:     init_config_without_command_line_processing();
igor@155:     $Context=$Config{l3cd};
igor@155:     connect_database();
igor@155: # Обработка всех grep'ов
igor@155:     $Grep="";
igor@155:     my $i=0;
igor@155:     my $arg;
igor@155:     my @argv=();
igor@155:     while ($i<=$#ARGV) {
igor@155:         $arg=$ARGV[$i];
igor@155:         #print "arg=$arg\n";
igor@155:         if ($arg eq "grep") {
igor@155:             my $grep_keys="";
igor@155:             my $grep_regexp="";
igor@155:             $i++;
igor@155:             while(defined ($ARGV[$i]) and $ARGV[$i] =~ /^-/) {
igor@155:                 $grep_keys .= " ".$ARGV[$i];
igor@155:                 $i++;
igor@155:             }
igor@155:             if (defined($ARGV[$i])) {
igor@155:                 $grep_regexp = $ARGV[$i];
igor@155:                 $i++;
igor@155:             }
igor@155:             else {
igor@155:                 die "ERROR: grep argument is not specified";
igor@155:             }
igor@155:             $Grep .= grep_options($grep_regexp,$grep_keys);
igor@155:         }
igor@155:         else {
igor@155:             $i++;
igor@155:             push(@new_argv,$arg);
igor@155:         }
igor@155:     }
igor@155:     @argv=@new_argv;
igor@155: 
igor@155:     if (grep(/^-f$/, @argv)) {
igor@155:         $follow=1;
igor@155:     }
igor@155:     if (grep(/^history$/, @argv)) {
igor@155:         my $interval=$argv[0]||"%";
igor@155:         $interval="%" if $interval eq "history";
igor@155:         my $exit=0;
igor@155:         while (not $exit) {
igor@155:             print_commands(select_interval($interval, $Grep));
igor@155:             if (not $follow) {
igor@155:                 $exit=1;
igor@155:             } else {
igor@155:                 sleep(1);
igor@155:             }
igor@155:         }
igor@155:     } else {
igor@155:         my $exit=0;
igor@155:         while (not $exit) {
igor@155:             print_all(select_interval($argv[0]||"%", $Grep));
igor@155:             if (not $follow) {
igor@155:                 $exit=1;
igor@155:             } else {
igor@155:                 sleep(1);
igor@155:             }
igor@155:         }
igor@155:     }
igor@155:     #print_all(select_by_cline("*privet*"));
igor@155: }
igor@155: 
igor@155: sub connect_database()
igor@155: {
igor@155:     $db = DBI->connect("dbi:SQLite:".$Config{cache_sqlite}, "", "",
igor@155:                 {RaiseError => 1, AutoCommit => 1});
igor@155:     $db->func('regexp', 2, sub {
igor@155:             my ($regex, $string) = @_;
igor@155:                 return $string =~ /$regex/;
igor@155:     }, 'create_function');
igor@155: }
igor@155: 
igor@155: sub select_all() {
igor@155:     $l3cd=$Config{l3cd};
igor@155:     return $db->selectall_arrayref("SELECT $Fields FROM commands WHERE l3cd='$l3cd'");
igor@155: }
igor@155: 
igor@155: sub select_last_n($) {
igor@155:     my $last=$_[0];
igor@155:     $count = $db->selectall_arrayref("SELECT COUNT(*) FROM commands");
igor@155:     my $offset=$$count[0][0]-$last;
igor@155:     return $db->selectall_arrayref("SELECT $Fields FROM commands LIMIT $last OFFSET $offset");
igor@155: }
igor@155: 
igor@155: sub select_interval($) {
igor@155:     my $interval = shift;
igor@155:     my $grep = shift;
igor@155: 
igor@155:     my $q;  # Рабочая переменная для запросов
igor@155: # %
igor@155:     $interval='1,$' if ($interval eq "%");
igor@155: #        return $db->selectall_arrayref(
igor@155: #                "SELECT $Fields FROM commands WHERE l3cd='$Context'");
igor@155: 
igor@155: # Вычисляем номера начальной и конечной строки
igor@155: # на основе интервала
igor@155:     my ($start,$stop);
igor@155:     my ($start_n, $stop_n, $curr_n) = ("","",""); # номера начальной и конечной строк
igor@155:     if ($interval =~ /,/) {
igor@155:         ($start,$stop) = split(/,/,$interval);
igor@155:     }
igor@155:     else {
igor@155:         $start = $interval;
igor@155:         $stop = $interval;
igor@155:     }
igor@155: 
igor@155:     $q = $db->selectall_arrayref("SELECT COUNT(*) FROM commands");
igor@155:     my $count = $$q[0][0];
igor@155: 
igor@155: # Номер текущей строки
igor@155: # По умолчанию текущая строка указывает на последнюю
igor@155:     $curr_n = $count;
igor@155:     #$curr_n = 2;
igor@155: 
igor@155:     if ($start =~ /^[0-9]*$/) { $start_n = $start; }
igor@155:     if ($stop =~ /^[0-9]*$/)  { $stop_n  = $stop; }
igor@155:     if ($start =~ /^-[0-9]*$/) { $start_n = $curr_n + $start; }
igor@155:     if ($stop =~ /^-[0-9]*$/)  { $stop_n  = $curr_n + $stop; }
igor@155:     if ($start =~ /^\+[0-9]*$/) { $start_n = $curr_n + $start; }
igor@155:     if ($stop =~ /^\+[0-9]*$/)  { $stop_n  = $curr_n + $stop; }
igor@155:     if ($start eq '.') { $start_n = $curr_n; }
igor@155:     if ($stop eq '.')  { $stop_n  = $curr_n; }
igor@155:     if ($start eq '$') { $start_n = $count; }
igor@155:     if ($stop eq '$')  { $stop_n  = $count; }
igor@155:     if ($start =~ m@^/(.*)/@ ) {
igor@155:         $q = $db->selectall_arrayref(
igor@155:                 "SELECT id FROM commands WHERE cline REGEXP '$1' LIMIT 1");
igor@155:         $start_n=$$q[0][0];
igor@155:     }
igor@155:     if ($stop =~ m@^/(.*)/@ ) {
igor@155:         $q = $db->selectall_arrayref(
igor@155:                 "SELECT id FROM commands 
igor@155:                  WHERE cline REGEXP '$1' 
igor@155:                         AND (id >= $start_n) LIMIT 1");
igor@155:         $stop_n=$$q[0][0];
igor@155:     }
igor@155:     if ($start =~ m@^o/(.*)/@ ) {
igor@155:         $q = $db->selectall_arrayref(
igor@155:                 "SELECT id FROM commands WHERE output REGEXP '$1' LIMIT 1");
igor@155:         $start_n=$$q[0][0];
igor@155:     }
igor@155:     if ($stop =~ m@^o/(.*)/@ && $start_n) {
igor@155:         $q = $db->selectall_arrayref(
igor@155:                 "SELECT id FROM commands 
igor@155:                  WHERE output REGEXP '$1' AND (id >= $start_n) LIMIT 1");
igor@155:         $stop_n=$$q[0][0];
igor@155:     }
igor@155: 
igor@155:     if (not $start_n or not $stop_n) {return ""};
igor@155: 
igor@155:     $stop_n = $count if $count < $stop_n;
igor@155:     $start_n =1 if 1 > $start_n;
igor@155: 
igor@155:     my $offset=$start_n-1;
igor@155:     my $limit = $stop_n - $offset;
igor@155: 
igor@155:     my $pre_follow=$Follow;
igor@155:     $q = $db->selectall_arrayref(
igor@155:             "SELECT id FROM commands 
igor@155:             WHERE ((id>=$start_n AND id<=$stop_n AND id>$pre_follow) $grep)
igor@155:             ORDER BY id DESC");
igor@155:     $Follow = $$q[0][0] if $$q[0][0];
igor@155:     return $db->selectall_arrayref(
igor@155:             "SELECT $Fields FROM commands 
igor@155:             WHERE ((id>=$start_n AND id<=$stop_n AND id>$pre_follow) $grep)");
igor@155: }
igor@155: 
igor@155: sub grep_options($$) {
igor@155: =cut
igor@155:     -c ищем только в комадной строке    # по умолчанию
igor@155:     -o ищем только в выводе
igor@155:     -f ищем и там, и там
igor@155:     -v инверсия
igor@155: =cut
igor@155:     my $regexp=shift; return "" if $regexp eq '';
igor@155:     my $options=shift;
igor@155:     my @options = split //, $options;
igor@155:     my $not = "";
igor@155:     $not = "NOT" if grep(/v/,@options);
igor@155: 
igor@155:     my $grep ="";
igor@155:     if (grep(/f/,@options)) {
igor@155:         $grep = "AND $not ((cline REGEXP '$regexp') OR (output REGEXP '$regexp'))";
igor@155:     }
igor@155:     elsif (grep(/o/,@options)) {
igor@155:         $grep = "AND $not (output REGEXP '$regexp')";
igor@155:     }
igor@155:     elsif (1 || grep(/c/,@options)) {
igor@155:         $grep = "AND $not (cline REGEXP '$regexp')";
igor@155:     }
igor@155: 
igor@155:     return $grep;
igor@155: }
igor@155: 
igor@155: sub select_by_cline($) {
igor@155:     my $cline = $_[0];
igor@155:     return $db->selectall_arrayref("SELECT $Fields FROM commands WHERE cline GLOB '$cline'");
igor@155: }
igor@155: 
igor@155: sub print_commands($)
igor@155: {
igor@155:     my @fields=@Fields;
igor@155:     my $fields=join(", ",@fields);
igor@155:     my $all=$_[0];
igor@155:     foreach my $row (@$all) {
igor@155:         my $cl;       #Каждая строка
igor@155:         my $i=0;
igor@155:         for my $f (@fields) {
igor@155:             $cl->{$fields[$i]}=$$row[$i];
igor@155:             $i++;
igor@155:         }
igor@155:         print $cl->{id}." ".strdequot($cl->{cline})."\n";
igor@155:     }
igor@155: }
igor@155: sub print_all($) {
igor@155:     my @fields=@Fields;
igor@155:     my $fields=join(", ",@fields);
igor@155:     my $all=$_[0];
igor@155:     my $prev_cl; # Предыдущая строка
igor@155:     my $tab_seq; # Номер в табуляционной последовательности
igor@155:     foreach my $row (@$all) {
igor@155:         my $cl;       #Каждая строка
igor@155:         my @anchor;   #Начальная строка, якорь, по которому привязывается команда
igor@155:         my $res="";   #Буфер, в который выводится результат вывода каждой строки
igor@155:         $i=0;
igor@155:         for my $f (@fields) {
igor@155:             $cl->{$fields[$i]}=$$row[$i];
igor@155:             $i++;
igor@155:         }
igor@155:         next    if ($Config{"skip_empty"} =~ /^y/i     && $cl->{"cline"} =~ /^\s*$/ )
igor@155:                 || ($Config{"skip_wrong"} =~ /^y/i     && $cl->{"err"} != 0)
igor@155:                 || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
igor@155: 
igor@155:         # Время
igor@155:         # Добавляем спереди 0 для удобочитаемости
igor@155:         my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
igor@155:         $min  = "0".$min  if $min  =~ /^.$/;
igor@155:         $hour = "0".$hour if $hour =~ /^.$/;
igor@155:         $sec  = "0".$sec  if $sec  =~ /^.$/;
igor@155: 
igor@155:         if ($Config{"show_time"} =~ /^y/i) {
igor@155:             push @anchor,"$hour:$min:$sec ";
igor@155:         }
igor@155: 
igor@155:         if ($cl->{"err"}) {
igor@155:             push @anchor, "err=".$cl->{'err'};
igor@155:         }
igor@155:         if ($cl->{"tab"}) {
igor@155:             push @anchor, "tab=".$cl->{'tab'};
igor@155:         }
igor@155:         $res.="#=".join("",@anchor)."\n";
igor@155:         $res.=$cl->{prompt}.strdequot($cl->{cline})."\n";
igor@155: 
igor@155:         my $output = ""; # фомируем вывод команды
igor@155:         my $last_command = $cl->{"last_command"};
igor@155:         if (!( $Config{"suppress_editors"} =~ /^y/i 
igor@155:                && grep ($_ eq $last_command, @{$Config{"editors"}}) 
igor@155:             || $Config{"suppress_pagers"}  =~ /^y/i 
igor@155:                && grep ($_ eq $last_command, @{$Config{"pagers"}})
igor@155:             || $Config{"suppress_terminal"}=~ /^y/i 
igor@155:                && grep ($_ eq $last_command, @{$Config{"terminal"}})
igor@155:             )) {
igor@155:             $output = strdequot($cl->{output});
igor@155: 
igor@155:             # Вырезаем слишком длинные выводы
igor@155:             my @lines = split '\n', $output;
igor@155:             if (!$Config{"command_id"} 
igor@155:                 && ( $Config{"head_lines"} || $Config{"tail_lines"})
igor@155:                 && $#lines >  $Config{"head_lines"} + $Config{"tail_lines"}) {
igor@155:                     $output="";
igor@155:                     for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
igor@155:                         $output .= $lines[$i]."\n";
igor@155:                     }
igor@155:                     $output .= $Config{"skip_text"}."\n";
igor@155: 
igor@155:                     my $start_line=$#lines-$Config{"tail_lines"}+1;
igor@155:                     for (my $i=$start_line; $i<= $#lines; $i++) {
igor@155:                         $output .= $lines[$i]."\n";
igor@155:                     }
igor@155:                 } 
igor@155:             }
igor@155:         $res.=$output;
igor@155:         if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
igor@155:             $res.= $cl->{"diff"}
igor@155:         }
igor@155:         #open(OUTPUT, '|sendxmpp nata_samoylenko@jabber.ru');
igor@155:         #print OUTPUT $res;
igor@155:         #close(OUTPUT);
igor@155:         print $res;
igor@155:         $res="";
igor@155:         $prev_cl=$cl;
igor@155:     }
igor@155: }
igor@155: 
igor@155: sub strdequot($)
igor@155: {
igor@155:     my $text = join "", @_;
igor@155:     $text =~ s/&amp;/&/g;
igor@155:     $text =~ s/&lt;/</g;
igor@155:     $text =~ s/&gt;/>/g;
igor@155:     $text =~ s/&quot;/"/g;
igor@155:     return $text;
igor@155: }
igor@155: 
igor@155: sub show_usage()
igor@155: {
igor@155:     print <<EOF;
igor@155: Usage:
igor@155:     l3 [INTERVAL] [grep [KEYS] REGEXP] [filter [KEYS] FILTER] [COMMAND [KEYS]]
igor@155: 
igor@155: Commands:
igor@155:     history
igor@155:     tail
igor@155:     head
igor@155:     cat (default)
igor@155: 
igor@155:     If COMMAND is not specified, cat assumed.
igor@155: 
igor@155: EOF
igor@155: }