lilalo
diff lm-report @ 1:ff93ad94d73b
LiLaLo -- Live Lab Log
(L3)
Система автоматического ведения журналов
и автоматизированного управления ходом
лабораторных работ.
(L3)
Система автоматического ведения журналов
и автоматизированного управления ходом
лабораторных работ.
author | devi |
---|---|
date | Sun May 22 16:29:55 2005 +0300 (2005-05-22) |
parents | |
children | a0f30dd46d1a |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/lm-report Sun May 22 16:29:55 2005 +0300 1.3 @@ -0,0 +1,912 @@ 1.4 +#!/usr/bin/perl -w 1.5 + 1.6 +# 1.7 +# (c) Igor Chubin, imchubin@mail.ru, 2004 1.8 +# 1.9 + 1.10 +use strict; 1.11 +use Getopt::Long; 1.12 +use Term::VT102; 1.13 +use Text::Iconv; 1.14 +use Data::Dumper; 1.15 + 1.16 +our $Config_File = "labmaker.conf"; 1.17 +our %Config = ( 1.18 + "skip_empty" => "yes", 1.19 + "skip_interrupted" => "no", 1.20 + "skip_wrong" => "no", 1.21 + "editors" => ["vi", "pico", "ee", "vim"], 1.22 + "pagers" => ["more", "less", "zmore", "zless", "info", 1.23 + "man", "mc", "trafshow", "screen", "cfdisk", 1.24 + "trafshow-bsd", "yes", "lynx", "links", "centericq" 1.25 + ], 1.26 + "terminal" => ["mc"], 1.27 + "suppress_editors" => "yes", 1.28 + "suppress_pagers" => "yes", 1.29 + "suppress_terminal" => "yes", 1.30 + 1.31 + "terminal_width" => 100, 1.32 + "terminal_height" => 100, 1.33 + "verbose" => "yes", 1.34 + 1.35 + "head_lines" => 5, 1.36 + "tail_lines" => 5, 1.37 + "skip_text" => "...", 1.38 + "show_time" => "yes", 1.39 + "show_diffs" => "yes", 1.40 + "show_comments" => "yes", 1.41 + 1.42 + "input" => "/root/.labmaker", 1.43 + "diffs" => "", 1.44 + "input_mask" => "*.script", 1.45 + "encoding" => "utf-8", 1.46 + 1.47 + "output" => "/var/www/lm/reportINDEX.html", 1.48 + #"output" => "report.xml", 1.49 + "output_mask" => "INDEX", 1.50 + "output_format" => "html", 1.51 + 1.52 + "signature" => "#lm:", 1.53 + "from" => "", 1.54 + "to" => "", 1.55 + "lab" => "", 1.56 + "keywords" => "linux command", 1.57 + "files_keywords" => "linux file", 1.58 + 1.59 + comment_width => "300", 1.60 + time_width => "60", 1.61 + 1.62 + ); 1.63 + 1.64 +our @Command_Lines; 1.65 +our @Command_Lines_Index; 1.66 +our @Diffs; 1.67 + 1.68 +our %Commands_Stat; # Statistics about commands usage 1.69 +our %Files_Stat; # Statistics about commands usage 1.70 + 1.71 +our %Search_Machines = ( 1.72 + "google" => { "query" => "http://www.google.com/search?q=" , 1.73 + "icon" => "google.ico" }, 1.74 + "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", 1.75 + "icon" => "freebsd.ico" }, 1.76 + "linux" => { "query" => "http://man.he.net/?topic=", 1.77 + "icon" => "linux.ico"}, 1.78 + "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=", 1.79 + "icon" => "opennet.ico"}, 1.80 + "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", 1.81 + "icon" => "freebsd.ico" }, 1.82 + 1.83 + ); 1.84 + 1.85 +our %Elements_Visibility = ( 1.86 + "note" => "замечания", 1.87 + "diff" => "редактор", 1.88 + "time" => "время", 1.89 + "ttychange" => "терминал", 1.90 + "wrong_output wrong_cline wrong_root_output wrong_root_cline" 1.91 + => "команды с ошибками", 1.92 + "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" 1.93 + => "прерванные команды", 1.94 + "tab_completion_output tab_completion_cline" 1.95 + => "продолжение с помощью tab" 1.96 +); 1.97 + 1.98 + 1.99 +sub load_diff_files 1.100 +{ 1.101 + my @pathes = @_; 1.102 + 1.103 + for my $path (@pathes) { 1.104 + my $template = "*.diff"; 1.105 + my @files = <$path/$template>; 1.106 + my $i=0; 1.107 + for my $file (@files) { 1.108 + my %diff; 1.109 + 1.110 + $diff{"path"}=$path; 1.111 + $diff{"uid"}="SET THIS"; 1.112 + 1.113 +# Сейчас UID определяется из названия каталога 1.114 +# откуда берутся diff-файлы 1.115 +# Это неправильно 1.116 +# 1.117 +# ВАРИАНТ: 1.118 +# К файлам жураналам должны прилагаться ситемны файлы, 1.119 +# мз которых и будет определяться соответствие 1.120 +# имён пользователей их uid'ам 1.121 +# 1.122 + $diff{"uid"} = 0 if $path =~ m@/root/@; 1.123 + 1.124 + $diff{"bind_to"}=""; 1.125 + $diff{"time_range"}=-1; 1.126 + 1.127 + next if not $file=~m@/(D?[0-9][0-9]?[0-9]?)[^/]*?([0-9]*):([0-9]*):?([0-9]*)@; 1.128 + $diff{"day"}=$1 || ""; 1.129 + $diff{"hour"}=$2; 1.130 + $diff{"min"}=$3; 1.131 + $diff{"sec"}=$4 || 0; 1.132 + 1.133 + $diff{"index"}=$i; 1.134 + 1.135 + print "diff loaded: $diff{day} $diff{hour}:$diff{min}:$diff{sec}\n"; 1.136 + 1.137 + local $/; 1.138 + open (F, "$file") 1.139 + or return "Can't open file $file ($_[0]) for reading"; 1.140 + my $text = <F>; 1.141 + if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i) { 1.142 + my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8"); 1.143 + $text = $converter->convert($text); 1.144 + } 1.145 + close(F); 1.146 + $diff{"text"}=$text; 1.147 + #print "$file loaded ($diff{day})\n"; 1.148 + 1.149 + push @Diffs, \%diff; 1.150 + $i++; 1.151 + } 1.152 + } 1.153 +} 1.154 + 1.155 + 1.156 +sub bind_diff 1.157 +{ 1.158 +# my $path = shift; 1.159 +# my $pid = shift; 1.160 +# my $day = shift; 1.161 +# my $lab = shift; 1.162 + 1.163 + print "Trying to bind diff...\n"; 1.164 + 1.165 + my $cl = shift; 1.166 + my $hour = $cl->{"hour"}; 1.167 + my $min = $cl->{"min"}; 1.168 + my $sec = $cl->{"sec"}; 1.169 + 1.170 + my $min_dt = 10000; 1.171 + 1.172 + for my $diff (@Diffs) { 1.173 + # Check here date, time and user 1.174 + next if ($diff->{"day"} && $cl->{"day"} && ($cl->{"day"} ne $diff->{"day"})); 1.175 + #next if (!$diff->{"uid"} && $cl->{"euid"} != $diff->{"uid"}); 1.176 + 1.177 + my $dt=($diff->{"hour"}-$hour)*3600 +($diff->{"min"}-$min)*60 + ($diff->{"sec"}-$sec); 1.178 + if ($dt >0 && $dt < $min_dt && ($diff->{"time_range"} <0 || $dt < $diff->{"time_range"})) { 1.179 + print "Approppriate diff found: dt=$dt\n"; 1.180 + if ($diff->{"bind_to"}) { 1.181 + undef $diff->{"bind_to"}->{"diff"}; 1.182 + }; 1.183 + $diff->{"time_range"}=$dt; 1.184 + $diff->{"bind_to"}=$cl; 1.185 + 1.186 + $cl->{"diff"} = $diff->{"index"}; 1.187 + $min_dt = $dt; 1.188 + } 1.189 + 1.190 + } 1.191 +} 1.192 + 1.193 + 1.194 +sub extract_from_cline 1.195 +# Разобрать командную строку $_[1] и возвратить хэш, содержащий 1.196 +# номер первого появление команды в строке: 1.197 +# команда => первая позиция 1.198 +{ 1.199 + my $what = $_[0]; 1.200 + my $cline = $_[1]; 1.201 + my @lists = split /\;/, $cline; 1.202 + 1.203 + 1.204 + my @commands = (); 1.205 + for my $list (@lists) { 1.206 + push @commands, split /\|/, $list; 1.207 + } 1.208 + 1.209 + my %commands; 1.210 + my %files; 1.211 + my $i=0; 1.212 + for my $command (@commands) { 1.213 + $command =~ /\s*(\S+)\s*(.*)/; 1.214 + if ($1 && $1 eq "sudo" ) { 1.215 + $commands{"$1"}=$i++; 1.216 + $command =~ s/\s*sudo\s+//; 1.217 + } 1.218 + $command =~ /\s*(\S+)\s*(.*)/; 1.219 + if ($1 && !defined $commands{"$1"}) { 1.220 + $commands{"$1"}=$i++; 1.221 + }; 1.222 + if ($2) { 1.223 + my $args = $2; 1.224 + my @args = split (/\s+/, $args); 1.225 + for my $a (@args) { 1.226 + $files{"$a"}=$i++ 1.227 + if !defined $files{"$a"}; 1.228 + }; 1.229 + 1.230 + 1.231 + } 1.232 + } 1.233 + 1.234 + if ($what eq "commands") { 1.235 + return %commands; 1.236 + } else { 1.237 + return %files; 1.238 + } 1.239 + 1.240 +} 1.241 + 1.242 +sub load_command_lines 1.243 +{ 1.244 + my $lab_scripts_path = $_[0]; 1.245 + my $lab_scripts_mask = $_[1]; 1.246 + 1.247 + my $cline_re_base = qq' 1.248 + (?:\\^?([0-9]*C?)) # exitcode 1.249 + (?:_([0-9]+)_)? # uid 1.250 + (?:_([0-9]+)_) # pid 1.251 + (...?) # day 1.252 + (..) # lab 1.253 + \\s # space separator 1.254 + ([0-9][0-9]):([0-9][0-9]):([0-9][0-9]) # time 1.255 + .\\[50D.\\[K # killing symbols 1.256 + (.*?([\$\#]\\s?)) # prompt 1.257 + (.*) # command line 1.258 + '; 1.259 + #my $cline_re = qr/$cline_re_base(?:$cline_re_base|$)/x; 1.260 + #my $cline_re = qr/(?:$cline_re_base)*$cline_re_base$/x; 1.261 + my $cline_re = qr/$cline_re_base/sx; 1.262 + my $cline_re1 = qr/$cline_re_base\x0D/sx; 1.263 + my $cline_re2 = qr/$cline_re_base$/sx; 1.264 + 1.265 + my $vt = Term::VT102->new ( 'cols' => $Config{"terminal_width"}, 1.266 + 'rows' => $Config{"terminal_height"}); 1.267 + my $cline_vt = Term::VT102->new ('cols' => $Config{"terminal_width"}, 1.268 + 'rows' => $Config{"terminal_height"}); 1.269 + 1.270 + my $converter = Text::Iconv->new($Config{"encoding"}, "utf-8") 1.271 + if ($Config{"encoding"} && $Config{"encoding"} !~ /^utf-8$/i); 1.272 + 1.273 + print "Loading lm-scripts...\n" if $Config{"verbose"} =~ /y/; 1.274 + 1.275 + my @lab_scripts = <$lab_scripts_path/$lab_scripts_mask>; 1.276 + my $file; 1.277 + my $files_number = $#lab_scripts; 1.278 + my $ii = 0; 1.279 + my $skip_info; 1.280 + 1.281 + my $commandlines_loaded =0; 1.282 + my $commandlines_processed =0; 1.283 + 1.284 + for $file (@lab_scripts){ 1.285 + #printf "\t%i %3.2f\n", $ii, (100*$ii++/$files_number) if $Config{"verbose"} =~ /y/; 1.286 + 1.287 + open (FILE, "$file"); 1.288 + binmode FILE; 1.289 + $file =~ m@.*/(.*?)-.*@; 1.290 + 1.291 + my $tty = $1; 1.292 + my $first_pass = 1; 1.293 + my %cl; 1.294 + my $last_output_length=0; 1.295 + while (<FILE>) { 1.296 + $commandlines_processed++; 1.297 + # time 1.298 + 1.299 + if (/[0-9][0-9]:[0-9][0-9]:[0-9][0-9].\[[0-9][0-9]D.\[K/ && m/$cline_re/) { 1.300 + s/.*\x0d(?!\x0a)//; 1.301 + # print "!!!",$_,"!!!\n"; 1.302 + # next; 1.303 + # while (m/$cline_re1/gs) { 1.304 + # } 1.305 + m/$cline_re2/gs; 1.306 + 1.307 + $commandlines_loaded++; 1.308 + $last_output_length=0; 1.309 + 1.310 + # Previous command 1.311 + my %last_cl = %cl; 1.312 + my $err = $1 || ""; 1.313 + 1.314 + # Parse new command 1.315 + $cl{"uid"} = $2; 1.316 + $cl{"euid"} = $cl{"uid"}; # Если в команде обнаружится sudo, euid поменяем на 0 1.317 + $cl{"pid"} = $3; 1.318 + $cl{"day"} = $4; 1.319 + $cl{"lab"} = $5; 1.320 + $cl{"hour"} = $6; 1.321 + $cl{"min"} = $7; 1.322 + $cl{"sec"} = $8; 1.323 + $cl{"fullprompt"} = $9; 1.324 + $cl{"prompt"} = $10; 1.325 + $cl{"raw_cline"} = $11; 1.326 + 1.327 + $cl{"err"} = 0; 1.328 + $cl{"output"} = ""; 1.329 + $cl{"tty"} = $tty; 1.330 + 1.331 + $cline_vt->process($cl{"raw_cline"}."\n"); 1.332 + $cl{"cline"} = $cline_vt->row_plaintext (1); 1.333 + $cl{"cline"} =~ s/\s*$//; 1.334 + $cline_vt->reset(); 1.335 + 1.336 + my %commands = extract_from_cline("commands", $cl{"cline"}); 1.337 + $cl{"euid"}=0 if defined $commands{"sudo"}; 1.338 + my @comms = sort { $commands{$a} cmp $commands{$b} } keys %commands; 1.339 + $cl{"last_command"} = $comms[$#comms] || ""; 1.340 + 1.341 + if ( 1.342 + $Config{"suppress_editors"} =~ /^y/i 1.343 + && grep ($_ eq $cl{"last_command"}, @{$Config{"editors"}}) || 1.344 + $Config{"suppress_pagers"} =~ /^y/i 1.345 + && grep ($_ eq $cl{"last_command"}, @{$Config{"pagers"}}) || 1.346 + $Config{"suppress_terminal"}=~ /^y/i 1.347 + && grep ($_ eq $cl{"last_command"}, @{$Config{"terminal"}}) 1.348 + ) { 1.349 + $cl{"suppress_output"} = "1"; 1.350 + } 1.351 + else { 1.352 + $cl{"suppress_output"} = "0"; 1.353 + 1.354 + } 1.355 + $skip_info = 0; 1.356 + 1.357 + 1.358 + print " ",$cl{"last_command"}; 1.359 + 1.360 + # Processing previous command line 1.361 + if ($first_pass) { 1.362 + $first_pass = 0; 1.363 + next; 1.364 + } 1.365 + 1.366 + # Error code 1.367 + $last_cl{"err"}=$err; 1.368 + $last_cl{"err"}=130 if $err eq "^C"; 1.369 + 1.370 + if (grep ($_ eq $last_cl{"last_command"}, @{$Config{"editors"}})) { 1.371 + bind_diff(\%last_cl); 1.372 + } 1.373 + 1.374 + # Output 1.375 + if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { 1.376 + for (my $i=0; $i<$Config{"terminal_height"}; $i++) { 1.377 + my $line= $vt->row_plaintext($i); 1.378 + next if !defined ($line) || $line =~ /^\s*$/; 1.379 + $line =~ s/\s*$//; 1.380 + $last_cl{"output"} .= $line."\n"; 1.381 + } 1.382 + } 1.383 + else { 1.384 + $last_cl{"output"}= ""; 1.385 + } 1.386 + 1.387 + $vt->reset(); 1.388 + 1.389 + 1.390 + # Classifying the command line 1.391 + 1.392 + 1.393 + # Save 1.394 + if (!$Config{"lab"} || $cl{"lab"} eq $Config{"lab"}) { 1.395 + # Changing encoding 1.396 + for (keys %last_cl) { 1.397 + $last_cl{$_} = $converter->convert($last_cl{$_}) 1.398 + if ($Config{"encoding"} && 1.399 + $Config{"encoding"} !~ /^utf-8$/i); 1.400 + } 1.401 + push @Command_Lines, \%last_cl; 1.402 + } 1.403 + next; 1.404 + } 1.405 + $last_output_length+=length($_); 1.406 + #if (!$cl{"suppress_output"} || $last_output_length < 5000) { 1.407 + if ($last_output_length < 5000) { 1.408 + #print "(",length($_),")" if (length($_) > 2000) ; 1.409 + $vt->process("$_"."\n") 1.410 + } 1.411 + else 1.412 + { 1.413 + if (!$skip_info) { 1.414 + print "($cl{last_command})"; 1.415 + $skip_info = 1; 1.416 + } 1.417 + } 1.418 + } 1.419 + close(FILE); 1.420 + 1.421 + } 1.422 + if ($Config{"verbose"} =~ /y/) { 1.423 + print "...finished." ; 1.424 + print "Lines loaded: $commandlines_processed\n"; 1.425 + print "Command lines: $commandlines_loaded\n"; 1.426 + } 1.427 +} 1.428 + 1.429 +sub search_by 1.430 +{ 1.431 + my $sm = shift; 1.432 + my $topic = shift; 1.433 + $topic =~ s/ /+/; 1.434 + 1.435 + return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='". 1.436 + $Search_Machines{$sm}->{"icon"}."' border='0'/></a>"; 1.437 +} 1.438 + 1.439 +sub make_comment 1.440 +{ 1.441 + my $commands = $_[0]; 1.442 + my $files = $_[1]; 1.443 + chomp $commands; 1.444 + chomp $files; 1.445 + return if (!$commands && !$files); 1.446 + 1.447 + my $comment=""; 1.448 + 1.449 + # Commands 1.450 + for my $command (split /\s+/,$commands) { 1.451 + $command =~ s/'//g; 1.452 + my $description=""; 1.453 + eval { $description=`mywi-client '$command'`; } ; 1.454 + $description = join ("<br>\n", grep(/\([18]\)/, split(/\n/, $description))); 1.455 + $description =~ s/.*?-//; 1.456 + next if $description =~ /^\s*$/; 1.457 + 1.458 + my $query=$command." ".$Config{"keywords"}; 1.459 + $query =~ s/\ /+/g; 1.460 + my $search= search_by("opennet",$query). 1.461 + search_by("local",$command). 1.462 + search_by("google",$query); 1.463 + 1.464 + $comment .= "<tr><td class='note_title'>$command</td>". 1.465 + "<td class='note_search'>$search</td>". 1.466 + "</tr><tr><td width='100%' colspan='2' class='note_text'>". 1.467 + "$description</td></tr><tr/>"; 1.468 + } 1.469 + 1.470 + # Files 1.471 + for my $file (split /\s+/,$files) { 1.472 + $file =~ s@.*/@@; 1.473 + $file =~ s/'//g; 1.474 + next if $file =~ /^\s*$/; 1.475 + next if $file =~ /^-/; 1.476 + 1.477 + my $description=`mywi '$file'`; 1.478 + $description = join ("<br>\n", grep(/\(5\)/, split(/\n/, $description))); 1.479 + next if $description =~ /^\s*$/; 1.480 + 1.481 + my $query=$file." ".$Config{"files_keywords"}; 1.482 + $query =~ s/\ /+/g; 1.483 + my $search= search_by("opennet",$query). 1.484 + search_by("local",$file). 1.485 + search_by("google",$query); 1.486 + 1.487 + $comment .= "<tr><td class='note_title'>$file</td>". 1.488 + "<td class='note_search'>$search</td>". 1.489 + "</tr><tr><td width='100%' colspan='2' class='note_text'>". 1.490 + "$description</td></tr><tr/>"; 1.491 + } 1.492 + 1.493 + 1.494 + return $comment; 1.495 +} 1.496 + 1.497 +sub printq 1.498 +{ 1.499 + my $TO = shift; 1.500 + my $text = join "", @_; 1.501 + $text =~ s/&/&/g; 1.502 + $text =~ s/</</g; 1.503 + $text =~ s/>/>/g; 1.504 + print $TO $text; 1.505 +} 1.506 + 1.507 + 1.508 +sub sort_command_lines 1.509 +{ 1.510 + print "Sorting command lines...\n" if $Config{"verbose"} =~ /y/; 1.511 + 1.512 + # Sort Command_Lines 1.513 + # Write Command_Lines to Command_Lines_Index 1.514 + 1.515 + my @index; 1.516 + for (my $i=0;$i<=$#Command_Lines;$i++) { 1.517 + $index[$i]=$i; 1.518 + } 1.519 + 1.520 + @Command_Lines_Index = sort { 1.521 + $Command_Lines[$index[$a]]->{"day"} cmp $Command_Lines[$index[$b]]->{"day"} || 1.522 + $Command_Lines[$index[$a]]->{"hour"} <=> $Command_Lines[$index[$b]]->{"hour"} || 1.523 + $Command_Lines[$index[$a]]->{"min"} <=> $Command_Lines[$index[$b]]->{"min"} || 1.524 + $Command_Lines[$index[$a]]->{"sec"} <=> $Command_Lines[$index[$b]]->{"sec"} 1.525 + } @index; 1.526 + 1.527 + print "...finished\n" if $Config{"verbose"} =~ /y/; 1.528 + 1.529 +} 1.530 + 1.531 +sub process_command_lines 1.532 +{ 1.533 + my $lab_scripts_path = $_[0]; 1.534 + 1.535 + for my $i (@Command_Lines_Index) { 1.536 + 1.537 + my $cl = \$Command_Lines[$i]; 1.538 + @{${$cl}->{"new_commands"}} =(); 1.539 + @{${$cl}->{"new_files"}} =(); 1.540 + $$cl->{"class"} = ""; 1.541 + 1.542 + if ($$cl->{"err"}) { 1.543 + $$cl->{"class"}="wrong"; 1.544 + $$cl->{"class"}="interrupted" 1.545 + if ($$cl->{"err"} eq 130); 1.546 + } 1.547 + if (!$$cl->{"euid"}) { 1.548 + $$cl->{"class"}.="_root"; 1.549 + } 1.550 + 1.551 +#tab# my @tab_words=split /\s+/, $$cl->{"output"}; 1.552 +#tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; 1.553 +#tab# $last_word =~ s@.*/@@; 1.554 +#tab# my $this_is_tab=1; 1.555 +#tab# 1.556 +#tab# if ($last_word && @tab_words >2) { 1.557 +#tab# for my $tab_words (@tab_words) { 1.558 +#tab# if ($tab_words !~ /^$last_word/) { 1.559 +#tab# $this_is_tab=0; 1.560 +#tab# last; 1.561 +#tab# } 1.562 +#tab# } 1.563 +#tab# } 1.564 +#tab# $$cl->{"class"}="tab" if $this_is_tab; 1.565 + 1.566 + 1.567 + if ( !$$cl->{"err"}) { 1.568 + # Command does not contain mistakes 1.569 + 1.570 + my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); 1.571 + my %files = extract_from_cline("files", ${$cl}->{"cline"}); 1.572 + 1.573 + # Searching for new commands only 1.574 + for my $command (keys %commands) { 1.575 + if (!defined $Commands_Stat{$command}) { 1.576 + push @{$$cl->{new_commands}}, $command; 1.577 + } 1.578 + $Commands_Stat{$command}++; 1.579 + } 1.580 + 1.581 + for my $file (keys %files) { 1.582 + if (!defined $Files_Stat{$file}) { 1.583 + push @{$$cl->{new_files}}, $file; 1.584 + } 1.585 + $Files_Stat{$file}++; 1.586 + } 1.587 + } 1.588 + } 1.589 + 1.590 +} 1.591 + 1.592 +sub print_command_lines 1.593 +{ 1.594 + my $output_filename=$_[0]; 1.595 + my $format = $Config{"output_format"}; 1.596 + 1.597 + open(OUT, ">", $output_filename) 1.598 + or die "Can't open $output_filename for writing\n"; 1.599 + 1.600 + 1.601 + 1.602 + if ($format eq "html") { 1.603 + # vvvv HTML Header 1.604 + print OUT <<HEADER; 1.605 + <html> 1.606 + <head> 1.607 + <meta content='text/html; charset=utf-8' http-equiv='Content-Type' /> 1.608 + <link rel='stylesheet' href='labmaker.css' type='text/css'/> 1.609 + </head> 1.610 + <body> 1.611 + <script> 1.612 + function getElementsByClassName(Class_Name) 1.613 + { 1.614 + var Result=new Array(); 1.615 + var All_Elements=document.all || document.getElementsByTagName('*'); 1.616 + for (i=0; i<All_Elements.length; i++) 1.617 + if (All_Elements[i].className==Class_Name) 1.618 + Result.push(All_Elements[i]); 1.619 + return Result; 1.620 + } 1.621 + function ShowHide (name) 1.622 + { 1.623 + elements=getElementsByClassName(name); 1.624 + for(i=0; i<elements.length; i++) 1.625 + if (elements[i].style.display == "none") 1.626 + elements[i].style.display = ""; 1.627 + else 1.628 + elements[i].style.display = "none"; 1.629 + //if (elements[i].style.visibility == "hidden") 1.630 + // elements[i].style.visibility = "visible"; 1.631 + //else 1.632 + // elements[i].style.visibility = "hidden"; 1.633 + } 1.634 + function filter_by_output(text) 1.635 + { 1.636 + 1.637 + var jjj=0; 1.638 + 1.639 + elements=getElementsByClassName('command'); 1.640 + for(i=0; i<elements.length; i++) { 1.641 + subelems = elements[i].getElementsByTagName('pre'); 1.642 + for(j=0; j<subelems.length; j++) { 1.643 + if (subelems[j].className = 'output') { 1.644 + var str = new String(subelems[j].nodeValue); 1.645 + if (jjj != 1) { 1.646 + alert(str); 1.647 + jjj=1; 1.648 + } 1.649 + if (str.indexOf(text) >0) 1.650 + subelems[j].style.display = "none"; 1.651 + else 1.652 + subelems[j].style.display = ""; 1.653 + 1.654 + } 1.655 + 1.656 + } 1.657 + } 1.658 + 1.659 + } 1.660 + </script> 1.661 +HEADER 1.662 + print OUT "<table class='visibility_form'><tr><td><form>\n"; 1.663 + for my $element (keys %Elements_Visibility) 1.664 + { 1.665 + my @e = split /\s+/, $element; 1.666 + my $showhide = join "", map { "ShowHide('$_');" } @e ; 1.667 + print OUT "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>", 1.668 + $Elements_Visibility{$element}, 1.669 + "</input><br>\n"; 1.670 + } 1.671 + #print OUT "<input type='text' size='10' name=\"by_command\"/>". 1.672 + #"<input type='button' value='фильтр по командам' onclick=\"filter_by_command()\"/> <br>\n"; 1.673 + #print OUT "<input type='text' size='10' name=\"by_output\"/>". 1.674 + #"<input type='button' value='фильтр по результату' ". 1.675 + #"onclick=\"filter_by_output(this.form.by_output.value)\"/> <br>\n"; 1.676 + 1.677 + print OUT "</form></td></tr></table>\n"; 1.678 + print OUT "<table width='100%'>\n"; 1.679 + # ^^^^ HTML Header 1.680 + } 1.681 + else { 1.682 + # XML Header 1.683 + print OUT "<script>\n" 1.684 + } 1.685 + 1.686 + my $cl; 1.687 + my $last_tty=""; 1.688 + my $in_range=0; 1.689 + for my $i (@Command_Lines_Index) { 1.690 + 1.691 + $cl = $Command_Lines[$i]; 1.692 + 1.693 + if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { 1.694 + $in_range=1; 1.695 + next; 1.696 + } 1.697 + if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { 1.698 + $in_range=0; 1.699 + next; 1.700 + } 1.701 + next if ($Config{"from"} && $Config{"to"} && !$in_range) 1.702 + || 1.703 + ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) 1.704 + || 1.705 + ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) 1.706 + || 1.707 + ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); 1.708 + 1.709 + my @new_commands=@{$cl->{"new_commands"}}; 1.710 + my @new_files=@{$cl->{"new_files"}}; 1.711 + 1.712 + my $cl_class="cline"; 1.713 + my $out_class="output"; 1.714 + if ($cl->{"class"}) { 1.715 + $cl_class = $cl->{"class"}."_".$cl_class; 1.716 + $out_class = $cl->{"class"}."_".$out_class; 1.717 + } 1.718 + 1.719 + my $output=""; 1.720 + if ($Config{"head_lines"} || $Config{"tail_lines"}) { 1.721 + # Partialy output 1.722 + my @lines = split '\n', $cl->{"output"}; 1.723 + # head 1.724 + my $mark=1; 1.725 + for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) { 1.726 + $output .= $lines[$i]."\n"; 1.727 + } 1.728 + # tail 1.729 + my $start=$#lines-$Config{"tail_lines"}+1; 1.730 + if ($start < 0) { 1.731 + $start=0; 1.732 + $mark=0; 1.733 + } 1.734 + if ($start < $Config{"head_lines"}) { 1.735 + $start=$Config{"head_lines"}; 1.736 + $mark=0; 1.737 + } 1.738 + $output .= $Config{"skip_text"}."\n" if $mark; 1.739 + for (my $i=$start; $i<= $#lines; $i++) { 1.740 + $output .= $lines[$i]."\n"; 1.741 + } 1.742 + } 1.743 + else { 1.744 + # Full output 1.745 + $output .= $cl->{"output"}; 1.746 + } 1.747 + $output .= "^C\n" if ($cl->{"err"} eq "130"); 1.748 + 1.749 + 1.750 +# Printing out 1.751 + 1.752 + # <command> 1.753 + print OUT $format eq "html" 1.754 + ? "<tr class='command'>\n" 1.755 + : "\n<action time='$cl->{hour}:$cl->{min}:$cl->{sec}' tty='$cl->{tty}'>\n"; 1.756 + 1.757 + 1.758 + if ($format eq "html") { 1.759 + # CONSOLE CHANGE 1.760 + if ( $last_tty ne $cl->{"tty"}) { 1.761 + print OUT "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>",$cl->{"tty"},"</td><td/></tr></table></td></tr><tr>"; 1.762 + $last_tty=$cl->{"tty"}; 1.763 + } 1.764 + 1.765 + # TIME 1.766 + if ($Config{"show_time"} =~ /^y/i) { 1.767 + print OUT "<td valign='top' class='time' width='$Config{time_width}'><pre>", 1.768 + $cl->{"hour"}, ":", $cl->{"min"}, ":", $cl->{"sec"}, 1.769 + "</td>"; 1.770 + } else { 1.771 + print OUT "<td width='0'/>" 1.772 + } 1.773 + } 1.774 + 1.775 + # COMMAND 1.776 + 1.777 + 1.778 + if ($format eq "html") { 1.779 + print OUT "<td class='script'>\n"; 1.780 + print OUT "<pre class='$cl_class'>\n"; 1.781 + my $cline = $cl->{"cline"}; 1.782 + $cline =~ s/\n//; 1.783 + printq(\*OUT,$cl->{"prompt"},$cl->{"cline"}); 1.784 +# printq(\*OUT,"(sudo ".$cl->{"last_command"}.")\n") if !$cl->{"euid"}; 1.785 + print OUT "</pre>\n"; 1.786 + } 1.787 + else { 1.788 + print OUT "<line class='$cl_class'>\n"; 1.789 + print OUT "<prompt>"; 1.790 + printq(\*OUT,$cl->{"prompt"}); 1.791 + print OUT "</prompt>"; 1.792 + print OUT "<command>"; 1.793 + printq(\*OUT,$cl->{"cline"}); 1.794 + print OUT "</command>"; 1.795 + print OUT "\n</line>\n"; 1.796 + } 1.797 + 1.798 + my $last_command = $cl->{"last_command"}; 1.799 + if (!( 1.800 + $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) || 1.801 + $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) || 1.802 + $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}}) 1.803 + )) { 1.804 + 1.805 + if ($format eq "html") { 1.806 + print OUT "<pre class='$out_class'>"; 1.807 + printq(\*OUT,$output); 1.808 + print OUT "</pre>\n"; 1.809 + } 1.810 + else { 1.811 + print OUT "<output class='$out_class'>\n"; 1.812 + printq(\*OUT,$output); 1.813 + print OUT "</output>\n"; 1.814 + } 1.815 + } 1.816 + 1.817 + # DIFF 1.818 + if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) { 1.819 + if ($format eq "html") { 1.820 + #print Dumper(%{$cl->{"diff"}}); 1.821 + print OUT "<table><tr><td width='5'/><td class='diff'><pre>"; 1.822 + printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); 1.823 + print OUT "</pre></td></tr></table>"; 1.824 + } 1.825 + else { 1.826 + print OUT "<diff>\n"; 1.827 + printq(\*OUT,${$Diffs[$cl->{"diff"}]}{"text"}); 1.828 + print OUT "</diff>\n"; 1.829 + } 1.830 + } 1.831 + 1.832 + # COMMENT 1.833 + if ( $Config{"show_comments"} =~ /^y/i) { 1.834 + my $comment = make_comment(join(" ",@new_commands), join (" ",@new_files)); 1.835 + if ($comment) { 1.836 + if ($format eq "html") { 1.837 + print OUT "<table width='$Config{comment_width}'>". 1.838 + "<tr><td width='5'/><td>"; 1.839 + print OUT "<table class='note' width='100%'>"; 1.840 + print OUT $comment; 1.841 + print OUT "</table>\n"; 1.842 + print OUT "</td></tr></table>"; 1.843 + } 1.844 + # else { 1.845 + # print OUT "<comment>"; 1.846 + # printq(\*OUT,$comment); 1.847 + # print OUT "</comment>"; 1.848 + # } 1.849 + } 1.850 + } 1.851 + 1.852 + if ($format eq "html") { 1.853 + print OUT "</td>\n"; 1.854 + print OUT "</tr>\n"; 1.855 + } 1.856 + else { 1.857 + print OUT "</action>\n"; 1.858 + } 1.859 + 1.860 + } 1.861 + if ($format eq "html") { 1.862 + print OUT "</table>\n"; 1.863 + print OUT "</body>\n"; 1.864 + print OUT "</html>\n"; 1.865 + } 1.866 + else { 1.867 + print OUT "</script>\n"; 1.868 + } 1.869 + close(OUT); 1.870 +} 1.871 + 1.872 +sub read_config_file 1.873 +{ 1.874 + my $config = $_[0]; 1.875 + my $filename = $_[1]; 1.876 + open(CONFIG, "$filename") 1.877 + or return; 1.878 + while (<CONFIG>) { 1.879 + s/#.*//; 1.880 + next if /^\s*$/; 1.881 + my ($var, $val) = split /\s*=\s*/, $_, 2; 1.882 + $var =~ s/\s*//; 1.883 + $config->{$var} = $val; 1.884 + } 1.885 + close(CONFIG); 1.886 +} 1.887 + 1.888 +$| = 1; 1.889 + 1.890 +my %file_config; 1.891 +my %argv_config; 1.892 +read_config_file(\%file_config, $Config_File); 1.893 +GetOptions(\%argv_config, map "$_=s", keys %Config); 1.894 +%Config = (%Config, %file_config, %argv_config); 1.895 + 1.896 + 1.897 +my $i=0; 1.898 + 1.899 +for my $lab_log (split (/\s+/, $Config{"diffs"} || $Config{"input"})) 1.900 +{ 1.901 + load_diff_files($lab_log); 1.902 +} 1.903 + 1.904 +for my $lab_log (split /\s+/, $Config{"input"}) 1.905 +{ 1.906 + my $tofile=$Config{"output"}; 1.907 + $tofile =~ s/$Config{"output_mask"}/$i/; 1.908 + #load_diff_files($lab_log); 1.909 + load_command_lines($lab_log, $Config{"input_mask"}); 1.910 + sort_command_lines; 1.911 + process_command_lines($lab_log); 1.912 + print_command_lines($tofile); 1.913 + $i++; 1.914 +} 1.915 +