#!/usr/bin/perl -w use IO::Socket; use lib '.'; use l3config; use locale; our @Command_Lines; our @Command_Lines_Index; our %Commands_Description; our %Args_Description; our $Mywi_Socket; our %Sessions; # vvv Инициализация переменных выполняется процедурой init_variables our @Day_Name; our @Month_Name; our @Of_Month_Name; our %Search_Machines; our %Elements_Visibility; # ^^^ our %Stat; our %CommandsFDistribution; # Сколько раз в журнале встречается какая команда sub search_buy; sub make_comment; sub load_command_lines_from_xml; sub load_sessions_from_xml; sub print_command_lines; sub sort_command_lines; sub process_command_lines; sub init_variables; sub main; sub collapse_list($); main(); sub main { $| = 1; init_variables(); init_config(); open_mywi_socket(); load_command_lines_from_xml($Config{"backend_datafile"}); load_sessions_from_xml($Config{"backend_datafile"}); sort_command_lines; process_command_lines; print_command_lines($Config{"output"}); close_mywi_socket; } sub search_by { my $sm = shift; my $topic = shift; $topic =~ s/ /+/; return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='". $Search_Machines{$sm}->{"icon"}."' border='0'/></a>"; } sub extract_from_cline # Разобрать командную строку $_[1] и возвратить хэш, содержащий # номер первого появление команды в строке: # команда => первая позиция { my $what = $_[0]; my $cline = $_[1]; my @lists = split /\;/, $cline; my @commands = (); for my $list (@lists) { push @commands, split /\|/, $list; } my %commands; my %args; my $i=0; for my $command (@commands) { $command =~ s@^\s*\S+/@@; $command =~ /\s*(\S+)\s*(.*)/; if ($1 && $1 eq "sudo" ) { $commands{"$1"}=$i++; $command =~ s/\s*sudo\s+//; } $command =~ s@^\s*\S+/@@; $command =~ /\s*(\S+)\s*(.*)/; if ($1 && !defined $commands{"$1"}) { $commands{"$1"}=$i++; }; if ($2) { my $args = $2; my @args = split (/\s+/, $args); for my $a (@args) { $args{"$a"}=$i++ if !defined $args{"$a"}; }; } } if ($what eq "commands") { return \%commands; } else { return \%args; } } sub open_mywi_socket { $Mywi_Socket = IO::Socket::INET->new( PeerAddr => $Config{mywi_server}, PeerPort => $Config{mywi_port}, Proto => "tcp", Type => SOCK_STREAM); } sub close_mywi_socket { close ($Mywi_Socket); } sub mywi_client { my $query = $_[0]; my $mywi; open_mywi_socket; if ($Mywi_Socket) { local $| = 1; local $/ = ""; print $Mywi_Socket $query."\n"; $mywi = <$Mywi_Socket>; $mywi = "" if $mywi =~ /nothing app/; } close_mywi_socket; return $mywi; } sub make_comment { my $cline = $_[0]; #my $files = $_[1]; my @comments=(); my @commands = keys %{extract_from_cline("commands", $cline)}; my @args = keys %{extract_from_cline("args", $cline)}; return if (!@commands && !@args); #return "commands=".join(" ",@commands)."; files=".join(" ",@files); # Commands for my $command (@commands) { $command =~ s/'//g; $CommandsFDistribution{$command}++; if (!$Commands_Description{$command}) { my $mywi=""; $mywi = mywi_client ($command); $mywi = join ("\n", grep(/\([18]\)/, split(/\n/, $mywi))); $mywi =~ s/\s+/ /; if ($mywi !~ /^\s*$/) { $Commands_Description{$command} = $mywi; } else { next; } } push @comments, $Commands_Description{$command}; } return join(" \n", @comments); # Files for my $arg (@args) { $arg =~ s/'//g; if (!$Args_Description{$arg}) { my $mywi; $mywi = mywi_client ($arg); $mywi = join ("\n", grep(/\([5]\)/, split(/\n/, $mywi))); $mywi =~ s/\s+/ /; if ($mywi !~ /^\s*$/) { $Args_Description{$arg} = $mywi; } else { next; } } push @comments, $Args_Description{$arg}; } } =cut Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта из XML-документа в переменную @Command_Lines Предупреждение! Процедура не в состоянии обрабатывать XML-документ любой структуры. В действительности файл cache из которого загружаются данные просто напоминает XML с виду. =cut sub load_command_lines_from_xml { my $datafile = $_[0]; open (CLASS, $datafile) or die "Can't open file of the class ",$datafile,"\n"; local $/; $data = <CLASS>; close(CLASS); for $command ($data =~ m@<command>(.*?)</command>@sg) { my %cl; while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) { $cl{$1} = $2; } push @Command_Lines, \%cl; } } sub load_sessions_from_xml { my $datafile = $_[0]; open (CLASS, $datafile) or die "Can't open file of the class ",$datafile,"\n"; local $/; my $data = <CLASS>; close(CLASS); for my $session ($data =~ m@<session>(.*?)</session>@sg) { my %session; while ($session =~ m@<([^>]*?)>(.*?)</\1>@sg) { $session{$1} = $2; } $Sessions{$session{local_session_id}} = \%session; } } sub sort_command_lines { # Sort Command_Lines # Write Command_Lines to Command_Lines_Index my @index; for (my $i=0;$i<=$#Command_Lines;$i++) { $index[$i]=$i; } @Command_Lines_Index = sort { $Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"} } @index; } sub process_command_lines { for my $i (@Command_Lines_Index) { my $cl = \$Command_Lines[$i]; #@{${$cl}->{"new_commands"}} =(); #@{${$cl}->{"new_files"}} =(); $$cl->{"class"} = ""; if ($$cl->{"err"}) { $$cl->{"class"}="wrong"; $$cl->{"class"}="interrupted" if ($$cl->{"err"} eq 130); } if (!$$cl->{"euid"}) { $$cl->{"class"}.="_root"; } #tab# my @tab_words=split /\s+/, $$cl->{"output"}; #tab# my $last_word= $$cl->{"cline"} =~ /(\S*)$/; #tab# $last_word =~ s@.*/@@; #tab# my $this_is_tab=1; #tab# #tab# if ($last_word && @tab_words >2) { #tab# for my $tab_words (@tab_words) { #tab# if ($tab_words !~ /^$last_word/) { #tab# $this_is_tab=0; #tab# last; #tab# } #tab# } #tab# } #tab# $$cl->{"class"}="tab" if $this_is_tab; # if ( !$$cl->{"err"}) { # # Command does not contain mistakes # # my %commands = extract_from_cline("commands", ${$cl}->{"cline"}); # my %files = extract_from_cline("files", ${$cl}->{"cline"}); # # # Searching for new commands only # for my $command (keys %commands) { # if (!defined $Commands_Stat{$command}) { # push @{$$cl->{new_commands}}, $command; # } # $Commands_Stat{$command}++; # } # # for my $file (keys %files) { # if (!defined $Files_Stat{$file}) { # push @{$$cl->{new_files}}, $file; # } # $Files_Stat{$file}++; # } # } if ($$cl->{cline}=~ m@cat[^#]*#([\^=v])\s*(.*)@) { if ($1 eq "=") { $$cl->{"class"} = "note"; $$cl->{"note"} = $$cl->{"output"}; $$cl->{"note_title"} = $2; } else { my $j = $i; if ($1 eq "^") { $j--; $j-- while ($j >=0 && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty})); } elsif ($1 eq "v") { $j++; $j++ while ($j <= @Command_Lines && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty})); } $Command_Lines[$j]->{note_title}="$2"; $Command_Lines[$j]->{note}=$$cl->{output}; $$cl=0; } } elsif ($$cl->{cline}=~ /#([\^=v])(.*)/) { if ($1 eq "=") { $$cl->{"class"} = "note"; $$cl->{"note"} = $2; } else { my $j=$i; if ($1 eq "^") { $j--; $j-- while ($j >=0 && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty})); } elsif ($1 eq "v") { $j++; $j++ while ($j <= @Command_Lines && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]); } $Command_Lines[$j]->{note}="$2"; $$cl=0; } } } } =cut Процедура print_command_lines выводит HTML-представление разобранного lab-скрипта. Разобранный lab-скрипт должен находиться в массиве @Command_Lines =cut sub print_command_lines { my $output_filename=$_[0]; my $course_name = $Config{"course-name"}; my $course_code = $Config{"course-code"}; my $course_date = $Config{"course-date"}; my $course_center = $Config{"course-center"}; my $course_trainer = $Config{"course-trainer"}; my $course_student = $Config{"course-student"}; # Результат выполнения процедуры равен # join("", @Result{header,body,stat,help,about,footer}) my %Result; my @toc; # Хранит оглавление my $note_number=0; $Result{"body"} = "<table width='100%'>\n"; my $cl; my $last_tty=""; my $last_day=""; my $in_range=0; my $i=0; COMMAND_LINE: for my $k (@Command_Lines_Index) { my $cl=$Command_Lines[$Command_Lines_Index[$i++]]; next unless $cl; if ($Config{filter}) { # Инициализация фильтра my %filter; for (split /&/,$Config{filter}) { my ($var, $val) = split /=/; $filter{$var} = $val; } if ($filter{hostname}) { next COMMAND_LINE unless $Sessions{$cl->{local_session_id}}->{hostname} eq $filter{hostname}; } if ($filter{user}) { next COMMAND_LINE unless $Sessions{$cl->{local_session_id}}->{user} eq $filter{user}; } #for my $filter_field (keys %filter) { # next COMMAND_LINE unless $Sessions{$cl->{local_session_id}}->{$filter_field} eq $filter{$filter_field}; #} } if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) { $in_range=1; next; } if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) { $in_range=0; next; } next if ($Config{"from"} && $Config{"to"} && !$in_range) || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); #my @new_commands=@{$cl->{"new_commands"}}; #my @new_files=@{$cl->{"new_files"}}; if ($cl->{class} eq "note") { my $note = $cl->{note}; $note = join ("\n", map ("<p>$_</p>", split (/-\n/, $note))); $note =~ s@(http:[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g; $note =~ s@(www\.[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g; $Result{"body"} .= "<tr><td colspan='6'>"; $Result{"body"} .= "<h4 id='note$note_number'>".$cl->{note_title}."</h4>" if $cl->{note_title}; $Result{"body"} .= "".$note."<p/><p/></td></td>"; if ($cl->{note_title}) { push @{$toc[@toc]},"<a href='#note$note_number'>".$cl->{note_title}."</a>"; $note_number++; } next; } my $cl_class="cline"; my $out_class="output"; if ($cl->{"class"}) { $cl_class = $cl->{"class"}."_".$cl_class; $out_class = $cl->{"class"}."_".$out_class; } my @new_commands; my @new_files; @new_commands = split (/\s+/, $cl->{"new_commands"}) if defined $cl->{"new_commands"}; @new_files = split (/\s+/, $cl->{"new_files"}) if defined $cl->{"new_files"}; my $output=""; if ($Config{"head_lines"} || $Config{"tail_lines"}) { # Partialy output my @lines = split '\n', $cl->{"output"}; # head my $mark=1; for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) { $output .= $lines[$i]."\n"; } # tail my $start=$#lines-$Config{"tail_lines"}+1; if ($start < 0) { $start=0; $mark=0; } if ($start < $Config{"head_lines"}) { $start=$Config{"head_lines"}; $mark=0; } $output .= $Config{"skip_text"}."\n" if $mark; for (my $i=$start; $i<= $#lines; $i++) { $output .= $lines[$i]."\n"; } } else { # Full output $output .= $cl->{"output"}; } #$output .= "^C\n" if ($cl->{"err"} eq "130"); # ## ## Начинается собственно вывод ## # # <command> my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time}); $Stat{FirstCommand} = $cl->{time} unless $Stat{FirstCommand}; $Stat{LastCommand} = 0 unless defined $Stat{LastCommand}; $Stat{TotalTime} += $cl->{time} - $Stat{LastCommand} if $cl->{time} - $Stat{LastCommand} < $Config{stat_inactivity_interval}; $Stat{LastCommand} = $cl->{time}; $Stat{TotalCommands} = 0 unless $Stat{TotalCommands}; $Stat{TotalCommands}++; # Добавляем спереди 0 для удобочитаемости $min = "0".$min if $min =~ /^.$/; $hour = "0".$hour if $hour =~ /^.$/; $sec = "0".$sec if $sec =~ /^.$/; $class=$cl->{"out_class"}; $class =~ s/output$//; $Stat{ErrorCommands}++ if $class =~ /wrong/; $Result{"body"} .= "<tr class='command'>\n"; # DAY CHANGE if ( $last_day ne $day) { #$Result{"body"} .= "<td colspan='6'><p></p><h3>День ",$day,"</h4></td></tr><tr>"; $Result{"body"} .= "<td colspan='6'><p></p><h3 id='day$day'>".$Day_Name[$wday]."</h4></td></tr><tr>"; push @toc, "<a href='#day$day'>".$Day_Name[$wday]."</a>\n"; $last_day=$day; } # CONSOLE CHANGE if ( $last_tty ne $cl->{"tty"}) { my $host; #$host = $Sessions{$cl->{local_session_id}}->{user}."@".$Sessions{$cl->{local_session_id}}->{hostname}; $Result{"body"} .= "<td colspan='6'><table><tr><td class='ttychange' width='140' align='center'>".$cl->{"tty"}."</td><td>$host</td></tr></table></td></tr><tr>"; $last_tty=$cl->{"tty"}; } # TIME if ($Config{"show_time"} =~ /^y/i) { $Result{"body"} .= "<td valign='top' class='time' width='$Config{time_width}'><pre>". $hour. ":". $min. ":". $sec. "</td>"; } else { $Result{"body"} .= "<td width='0'/>" } # COMMAND $Result{"body"} .= "<td class='script'>\n"; $Result{"body"} .= "<pre class='${class}cline'>\n"; my $cline = $cl->{"prompt"}.$cl->{"cline"}; $cline =~ s/\n//; #$cline .= "(".$Sessions{$cl->{local_session_id}}.")"; my $hint = make_comment($cl->{"cline"}); $cline = "<div title='$hint'>$cline</div>" if $hint; $Result{"body"} .= $cline; $Result{"body"} .= "</pre>\n"; my $last_command = $cl->{"last_command"}; if (!( $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) || $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) || $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}}) )) { $Result{"body"} .= "<pre class='".$cl->{out_class}."'>"; $Result{"body"} .= $output; $Result{"body"} .= "</pre>\n"; } # DIFF if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) { $Result{"body"} .= "<table><tr><td width='5'/><td class='diff'><pre>"; $Result{"body"} .= $cl->{"diff"}; $Result{"body"} .= "</pre></td></tr></table>"; } #NOTES if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) { my $note=$cl->{"note"}; $note =~ s/\n/<br\/>\n/msg; $note =~ s@(http:[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g; $note =~ s@(www\.[a-zA-Z.0-9/?%-]*)@<a href='$1'>$1</a>@g; # Ширину пока не используем # $Result{"body"} .= "<table width='$Config{note_width}' class='note'>"; $Result{"body"} .= "<table class='note'>"; $Result{"body"} .= "<tr><td class='note_title'>".$cl->{note_title}."</td></tr>" if $cl->{note_title}; $Result{"body"} .= "<tr><td width='100%' class='note_text'>".$note."</td></tr>"; $Result{"body"} .= "</table>\n"; } # COMMENT if ( $Config{"show_comments"} =~ /^y/i) { my $comment = make_comment($cl->{"cline"}); if ($comment) { $Result{"body"} .= "<table width='$Config{comment_width}'>". "<tr><td width='5'/><td>"; $Result{"body"} .= "<table class='note' width='100%'>"; $Result{"body"} .= $comment; $Result{"body"} .= "</table>\n"; $Result{"body"} .= "</td></tr></table>"; } } # Вывод очередной команды окончен $Result{"body"} .= "</td>\n"; $Result{"body"} .= "</tr>\n"; } $Result{"body"} .= "</table>\n"; #$Result{"stat"} = "<hr/>"; %StatNames = ( FirstCommand => "Время первой команды журнала", LastCommand => "Время последней команды журнала", TotalCommands => "Количество командных строк в журнале", ErrorsPercentage => "Процент команд с ненулевым кодом завершения, %", TotalTime => "Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час", CommandsPerTime => "Количество командных строк в единицу времени, команда/мин", CommandsFDistribution => "Частота использования команд", CommandsFDistribution => "Частота использования команд", RareCommands => "Частота использования этих команд < 0.5%", ); @StatOrder = ( FirstCommand, LastCommand, TotalCommands, ErrorsPercentage, TotalTime, CommandsPerTime, CommandsFDistribution, RareCommands, ); # Подготовка статистики к выводу # Некоторые значения пересчитываются! # Дальше их лучше уже не использовать!!! my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{FirstCommand}); $Stat{FirstCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec, $year+1900, $mon+1, $mday; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{LastCommand}); $Stat{LastCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec, $year+1900, $mon+1, $mday; $Stat{ErrorsPercentage} = sprintf "%5.2f", $Stat{ErrorCommands}*100/$Stat{TotalCommands} if $Stat{TotalCommands}; $Stat{CommandsPerTime} = sprintf "%5.2f", $Stat{TotalCommands}*60/$Stat{TotalTime} if $Stat{TotalTime}; $Stat{TotalTime} = sprintf "%5.2f", $Stat{TotalTime}/60/60; my $total_commands=0; for $command (keys %CommandsFDistribution){ $total_commands += $CommandsFDistribution{$command}; } if ($total_commands) { for $command (reverse sort {$CommandsFDistribution{$a} <=> $CommandsFDistribution{$b}} keys %CommandsFDistribution){ my $command_html; my $percentage = sprintf "%5.2f",$CommandsFDistribution{$command}*100/$total_commands; if ($percentage < 0.5) { my $hint = make_comment($command); $command_html = "$command"; $command_html = "<span title='$hint' class='hint'>$command_html</span>" if $hint; my $command_html = "<tt>$command_html</tt>"; $Stat{RareCommands} .= $command_html."<sub><font size='-2'>".$CommandsFDistribution{$command}."</font></sub> , "; } else { my $hint = make_comment($command); $command_html = "$command"; $command_html = "<span title='$hint' class='hint'>$command_html</span>" if $hint; my $command_html = "<tt>$command_html</tt>"; $percentage = sprintf "%5.2f",$percentage; $Stat{CommandsFDistribution} .= "<tr><td>".$command_html."</td><td>".$CommandsFDistribution{$command}."</td>". "<td>|".("="x int($CommandsFDistribution{$command}*100/$total_commands))."| $percentage%</td></tr>"; } } $Stat{CommandsFDistribution} = "<table>".$Stat{CommandsFDistribution}."</table>"; $Stat{RareCommands} =~ s/, $// if $Stat{RareCommands}; } $Result{"stat"} .= "<h2 id='stat'>Статистика</h2>"; $Result{"stat"} .= "<table>"; for my $stat (@StatOrder) { $Result{"stat"} .= "<tr valign='top'><td width='300'>".$StatNames{"$stat"}."</td><td>".$Stat{"$stat"}."</td></tr>" if $Stat{"$stat"}; } $Result{"stat"} .= "</table>"; $Result{"stat"} .= "<font size='-2'>____<br/>*) Интервалы неактивности длительностью ".($Config{stat_inactivity_interval}/60)." минут и более не учитываются</font></br>"; #$Result{"help"} .= "<hr/>"; $Result{"help"} .= "<h2 id='help'>Справка</h2>"; $Result{"help"} .= "$Html_Help<br/>"; #$Result{"about"} .= "<hr/>"; $Result{"about"} .= "<h2 id='about'>О программе</h2>"; $Result{"about"} .= "$Html_About"; $Result{"footer"} .= "</body>\n"; $Result{"footer"} .= "</html>\n"; $Result{"title"} = "Журнал лабораторных работ"; $Result{"title"}.= " -- ".$course_student if $course_student; if ($course_date) { $Result{"title"}.= " -- ".$course_date; $Result{"title"}.= "/".$course_code if $course_code; } else { $Result{"title"}.= " -- ".$course_code if $course_code; } # Заголовок генерируется позже всего # Тогда, когда известно уже, что должно быть написано в # оглавлении $Result{"header"} = <<HEADER; <html> <head> <meta content='text/html; charset=utf-8' http-equiv='Content-Type' /> <link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/> <title>$Result{title}</title> </head> <body> <script> $Html_JavaScript </script> <h1>Журнал лабораторных работ</h1> HEADER $Result{"header"} .= "<p>" if $course_student || $course_trainer || $course_name || $course_code || $course_date || $course_center; $Result{"header"} .= "Выполнил $course_student<br/>" if $course_student; $Result{"header"} .= "Проверил $course_trainer <br/>" if $course_trainer; $Result{"header"} .= "Курс " if $course_name || $course_code || $course_date; $Result{"header"} .= "$course_name " if $course_name; $Result{"header"} .= "($course_code)" if $course_code; $Result{"header"} .= ", $course_date<br/>" if $course_date; $Result{"header"} .= "Учебный центр $course_center <br/>" if $course_center; $Result{"header"} .= "</p>" if $course_student || $course_trainer || $course_name || $course_code || $course_date || $course_center; my $toc = collapse_list (\@toc); $Result{"header"} .= <<HEADER; <ul> <li><a href='#log'>Журнал</a></li> <ul>$toc</ul> <li><a href='#stat'>Статистика</a></li> <li><a href='#help'>Справка</a></li> <li><a href='#about'>О программе</a></li> </ul> <h2 id="log">Журнал</h2> HEADER $Result{"header"} .= "<table id='visibility_form' class='visibility_form'><tr><td><form>\n"; for my $element (keys %Elements_Visibility) { my @e = split /\s+/, $element; my $showhide = join "", map { "ShowHide('$_');" } @e ; $Result{"header"} .= "<input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>". $Elements_Visibility{$element}. "</input><br>\n"; } $Result{"header"} .= "</form></td></tr></table>\n"; if ($output_filename eq "-") { print $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"}; } else { open(OUT, ">", $output_filename) or die "Can't open $output_filename for writing\n"; print OUT $Result{"header"}, $Result{"body"}, $Result{"stat"}, $Result{"help"}, $Result{"about"}, $Result{"footer"}; close(OUT); } } sub collapse_list($) { my $res = ""; for my $elem (@{$_[0]}) { if (ref $elem eq "ARRAY") { $res .= "<ul>".collapse_list($elem)."</ul>"; } else { $res .= "<li>".$elem."</li>"; } } return $res; } sub init_variables { $Html_Help = <<HELP; Для того чтобы использовать LiLaLo, не нужно знать ничего особенного: всё происходит само собой. Однако, чтобы ведение и последующее использование журналов было как можно более эффективным, желательно иметь в виду следующее: <ul> <li><p> В журнал автоматически попадают все команды, данные в любом терминале системы. </p></li> <li><p> Для того чтобы убедиться, что журнал на текущем терминале ведётся, и команды записываются, дайте команду w. В поле WHAT, соответствующем текущему терминалу, должна быть указана программа script. </p></li> <li><p> Если код завершения команды равен нулю, команда была выполнена без ошибок. Команды, код завершения которых отличен от нуля, выделяются цветом. <table> <tr class='command'> <td class='script'> <pre class='wrong_cline'> \$ l s-l</pre> <pre class='wrong_output'>bash: l: command not found </pre> </td> </tr> </table> <br/> </p></li> <li><p> Команды, ход выполнения которых был прерван пользователем, выделяются цветом. <table> <tr class='command'> <td class='script'> <pre class='interrupted_cline'> \$ find / -name abc</pre> <pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung find: /home/devi-orig/.gnome2_private: Keine Berechtigung find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung find: /home/devi-orig/.metacity: Keine Berechtigung find: /home/devi-orig/.inkscape: Keine Berechtigung ^C </pre> </td> </tr> </table> <br/> </p></li> <li><p> Команды, выполненные с привилегиями суперпользователя, выделяются слева красной чертой. <table> <tr class='command'> <td class='script'> <pre class='_root_cline'> # id</pre> <pre class='_root_output'> uid=0(root) gid=0(root) Gruppen=0(root) </pre> </td> </tr> </table> <br/> </p></li> <li><p> Изменения, внесённые в текстовый файл с помощью редактора, запоминаются и показываются в журнале в формате ed. Строки, начинающиеся символом "<", удалены, а строки, начинающиеся символом ">" -- добавлены. <table> <tr class='command'> <td class='script'> <pre class='cline'> \$ vi ~/.bashrc</pre> <table><tr><td width='5'/><td class='diff'><pre>2a3,5 > if [ -f /usr/local/etc/bash_completion ]; then > . /usr/local/etc/bash_completion > fi </pre></td></tr></table></td> </tr> </table> <br/> </p></li> <li><p> Для того чтобы получить краткую справочную информацию о команде, нужно подвести к ней мышь. Во всплывающей подсказке появится краткое описание команды. </p></li> <li><p> Время ввода команды, показанное в журнале, соответствует времени <i>начала ввода командной строки</i>, которое равно тому моменту, когда на терминале появилось приглашение интерпретатора </p></li> <li><p> Имя терминала, на котором была введена команда, показано в специальном блоке. Этот блок показывается только в том случае, если терминал текущей команды отличается от терминала предыдущей. </p></li> <li><p> Вывод не интересующих вас в настоящий момент элементов журнала, таких как время, имя терминала и других, можно отключить. Для этого нужно воспользоваться <a href='#visibility_form'>формой управления журналом</a> вверху страницы. </p></li> <li><p> Небольшие комментарии к командам можно вставлять прямо из командной строки. Комментарий вводится прямо в командную строку, после символов #^ или #v. Символы ^ и v показывают направление выбора команды, к которой относится комментарий: ^ - к предыдущей, v - к следующей. Например, если в командной строке было введено: <pre class='cline'> \$ whoami </pre> <pre class='output'> user </pre> <pre class='cline'> \$ #^ Интересно, кто я? </pre> в журнале это будет выглядеть так: <pre class='cline'> \$ whoami </pre> <pre class='output'> user </pre> <table class='note'><tr><td width='100%' class='note_text'> <tr> <td> Интересно, кто я?<br/> </td></tr></table> </p></li> <li><p> Если комментарий содержит несколько строк, его можно вставить в журнал следующим образом: <pre class='cline'> \$ whoami </pre> <pre class='output'> user </pre> <pre class='cline'> \$ cat > /dev/null #^ Интересно, кто я? </pre> <pre class='output'> Программа whoami выводит имя пользователя, под которым мы зарегистрировались в системе. - Она не может ответить на вопрос о нашем назначении в этом мире. </pre> В журнале это будет выглядеть так: <table> <tr class='command'> <td class='script'> <pre class='cline'> \$ whoami</pre> <pre class='output'>user </pre> <table class='note'><tr><td class='note_title'>Интересно, кто я?</td></tr><tr><td width='100%' class='note_text'> Программа whoami выводит имя пользователя, под которым<br/> мы зарегистрировались в системе.<br/> <br/> Она не может ответить на вопрос о нашем назначении<br/> в этом мире.<br/> </td></tr></table> </td> </tr> </table> Для разделения нескольких абзацев между собой используйте символ "-", один в строке. <br/> </p></li> <li><p> Комментарии, не относящиеся непосредственно ни к какой из команд, добавляются точно таким же способом, только вместо симолов #^ или #v нужно использовать символы #= </p></li> </ul> HELP $Html_About = <<ABOUT; <p> LiLaLo (L3) расшифровывается как Live Lab Log.<br/> Программа разработана для повышения эффективности обучения Unix/Linux-системам.<br/> (c) Игорь Чубин, 2004-2005<br/> </p> ABOUT $Html_About.='$Id$ </p>'; $Html_JavaScript = <<JS; function getElementsByClassName(Class_Name) { var Result=new Array(); var All_Elements=document.all || document.getElementsByTagName('*'); for (i=0; i<All_Elements.length; i++) if (All_Elements[i].className==Class_Name) Result.push(All_Elements[i]); return Result; } function ShowHide (name) { elements=getElementsByClassName(name); for(i=0; i<elements.length; i++) if (elements[i].style.display == "none") elements[i].style.display = ""; else elements[i].style.display = "none"; //if (elements[i].style.visibility == "hidden") // elements[i].style.visibility = "visible"; //else // elements[i].style.visibility = "hidden"; } function filter_by_output(text) { var jjj=0; elements=getElementsByClassName('command'); for(i=0; i<elements.length; i++) { subelems = elements[i].getElementsByTagName('pre'); for(j=0; j<subelems.length; j++) { if (subelems[j].className = 'output') { var str = new String(subelems[j].nodeValue); if (jjj != 1) { alert(str); jjj=1; } if (str.indexOf(text) >0) subelems[j].style.display = "none"; else subelems[j].style.display = ""; } } } } JS %Search_Machines = ( "google" => { "query" => "http://www.google.com/search?q=" , "icon" => "$Config{frontend_google_ico}" }, "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", "icon" => "$Config{frontend_freebsd_ico}" }, "linux" => { "query" => "http://man.he.net/?topic=", "icon" => "$Config{frontend_linux_ico}"}, "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=", "icon" => "$Config{frontend_opennet_ico}"}, "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=", "icon" => "$Config{frontend_local_ico}" }, ); %Elements_Visibility = ( "note" => "замечания", "diff" => "редактор", "time" => "время", "ttychange" => "терминал", "wrong_output wrong_cline wrong_root_output wrong_root_cline" => "команды с ошибками", "interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline" => "прерванные команды", "tab_completion_output tab_completion_cline" => "продолжение с помощью tab" ); @Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /; @Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /; @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /; }