lilalo
view l3-frontend @ 100:2c00c61f2d7b
Коммичу изменения, но сам не знаю зачем.
Нужно l3-cgi переписать вообще с нуля.
Он мерзкий.
И продумать нужно, как он вообще должен работать.
Понятно, приблизительно, как он должен показывать журнал,
когда до него уже дошли,
но вот если не дошли, то что делать не понятно.
Короче, продумать систему навигации.
Нужно l3-cgi переписать вообще с нуля.
Он мерзкий.
И продумать нужно, как он вообще должен работать.
Понятно, приблизительно, как он должен показывать журнал,
когда до него уже дошли,
но вот если не дошли, то что делать не понятно.
Короче, продумать систему навигации.
author | devi |
---|---|
date | Wed Jun 14 21:37:22 2006 +0300 (2006-06-14) |
parents | 45196265d30e |
children | c41cc9a4b5ea |
line source
1 #!/usr/bin/perl -w
3 use IO::Socket;
4 use lib '.';
5 use l3config;
6 use utf8;
8 our @Command_Lines;
9 our @Command_Lines_Index;
10 our %Commands_Description;
11 our %Args_Description;
12 our $Mywi_Socket;
13 our %Sessions;
15 our %filter;
16 our $filter_url;
17 sub init_filter;
19 our %Files;
21 # vvv Инициализация переменных выполняется процедурой init_variables
22 our @Day_Name;
23 our @Month_Name;
24 our @Of_Month_Name;
25 our %Search_Machines;
26 our %Elements_Visibility;
27 # ^^^
29 our %Stat;
30 our %frequency_of_command; # Сколько раз в журнале встречается какая команда
31 our $table_number=1;
33 my %mywi_cache_for; # Кэш для экономии обращений к mywi
35 sub make_comment;
36 sub make_new_entries_table;
37 sub load_command_lines_from_xml;
38 sub load_sessions_from_xml;
39 sub sort_command_lines;
40 sub process_command_lines;
41 sub init_variables;
42 sub main;
43 sub collapse_list($);
45 sub minutes_passed;
47 sub print_all_txt;
48 sub print_all_html;
49 sub print_edit_all_html;
50 sub print_command_lines_html;
51 sub print_command_lines_txt;
52 sub print_files_html;
53 sub print_stat_html;
54 sub print_header_html;
55 sub print_footer_html;
57 main();
59 sub main
60 {
61 $| = 1;
63 init_variables();
64 init_config();
65 $Config{frontend_ico_path}=$Config{frontend_css};
66 $Config{frontend_ico_path}=~s@/[^/]*$@@;
67 init_filter();
69 open_mywi_socket();
70 load_command_lines_from_xml($Config{"backend_datafile"});
71 load_sessions_from_xml($Config{"backend_datafile"});
72 sort_command_lines;
73 process_command_lines;
74 if (defined($filter{action}) && $filter{action} eq "edit") {
75 print_edit_all_html($Config{"output"});
76 }
77 else {
78 print_all_html($Config{"output"});
79 }
80 close_mywi_socket;
81 }
83 sub init_filter
84 {
85 if ($Config{filter}) {
86 # Инициализация фильтра
87 for (split /&/,$Config{filter}) {
88 my ($var, $val) = split /=/;
89 $filter{$var} = $val || "";
90 }
91 }
92 $filter_url = join ("&", map("$_=$filter{$_}", keys %filter));
93 }
95 # extract_from_cline
97 # In: $what = commands | args
98 # Out: return ссылка на хэш, содержащий результаты разбора
99 # команда => позиция
101 # Разобрать командную строку $_[1] и возвратить хэш, содержащий
102 # номер первого появление команды в строке:
103 # команда => первая позиция
104 sub extract_from_cline
105 {
106 my $what = $_[0];
107 my $cline = $_[1];
108 my @lists = split /\;/, $cline;
111 my @command_lines = ();
112 for my $command_list (@lists) {
113 push(@command_lines, split(/\|/, $command_list));
114 }
116 my %position_of_command;
117 my %position_of_arg;
118 my $i=0;
119 for my $command_line (@command_lines) {
120 $command_line =~ s@^\s*@@;
121 $command_line =~ /\s*(\S+)\s*(.*)/;
122 if ($1 && $1 eq "sudo" ) {
123 $position_of_command{"$1"}=$i++;
124 $command_line =~ s/\s*sudo\s+//;
125 }
126 if ($command_line !~ m@^\s*\S*/etc/@) {
127 $command_line =~ s@^\s*\S+/@@;
128 }
130 $command_line =~ /\s*(\S+)\s*(.*)/;
131 my $command = $1;
132 my $args = $2;
133 if ($command && !defined $position_of_command{"$command"}) {
134 $position_of_command{"$command"}=$i++;
135 };
136 if ($args) {
137 my @args = split (/\s+/, $args);
138 for my $a (@args) {
139 $position_of_arg{"$a"}=$i++
140 if !defined $position_of_arg{"$a"};
141 };
142 }
143 }
145 if ($what eq "commands") {
146 return \%position_of_command;
147 } else {
148 return \%position_of_arg;
149 }
151 }
156 #
157 # Подпрограммы для работы с mywi
158 #
160 sub open_mywi_socket
161 {
162 $Mywi_Socket = IO::Socket::INET->new(
163 PeerAddr => $Config{mywi_server},
164 PeerPort => $Config{mywi_port},
165 Proto => "tcp",
166 Type => SOCK_STREAM);
167 }
169 sub close_mywi_socket
170 {
171 close ($Mywi_Socket) if $Mywi_Socket ;
172 }
175 sub mywi_client
176 {
177 return "";
178 my $query = $_[0];
179 my $mywi;
181 open_mywi_socket;
182 if ($Mywi_Socket) {
183 local $| = 1;
184 local $/ = "";
185 print $Mywi_Socket $query."\n";
186 $mywi = <$Mywi_Socket>;
187 $mywi = "" if $mywi =~ /nothing app/;
188 }
189 close_mywi_socket;
190 return $mywi;
191 }
193 sub make_comment
194 {
195 my $cline = $_[0];
196 #my $files = $_[1];
198 my @comments;
199 my @commands = keys %{extract_from_cline("commands", $cline)};
200 my @args = keys %{extract_from_cline("args", $cline)};
201 return if (!@commands && !@args);
202 #return "commands=".join(" ",@commands)."; files=".join(" ",@files);
204 # Commands
205 for my $command (@commands) {
206 $command =~ s/'//g;
207 $frequency_of_command{$command}++;
208 if (!$Commands_Description{$command}) {
209 $mywi_cache_for{$command} ||= mywi_client ($command) || "";
210 my $mywi = join ("\n", grep(/\([18]|sh|script\)/, split(/\n/, $mywi_cache_for{$command})));
211 $mywi =~ s/\s+/ /;
212 if ($mywi !~ /^\s*$/) {
213 $Commands_Description{$command} = $mywi;
214 }
215 else {
216 next;
217 }
218 }
220 push @comments, $Commands_Description{$command};
221 }
222 return join(" \n", @comments);
224 # Files
225 for my $arg (@args) {
226 $arg =~ s/'//g;
227 if (!$Args_Description{$arg}) {
228 my $mywi;
229 $mywi = mywi_client ($arg);
230 $mywi = join ("\n", grep(/\([5]\)/, split(/\n/, $mywi)));
231 $mywi =~ s/\s+/ /;
232 if ($mywi !~ /^\s*$/) {
233 $Args_Description{$arg} = $mywi;
234 }
235 else {
236 next;
237 }
238 }
240 push @comments, $Args_Description{$arg};
241 }
243 }
245 =cut
246 Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
247 из XML-документа в переменную @Command_Lines
249 # In: $datafile имя файла
250 # Out: @CommandLines загруженные командные строки
252 Предупреждение!
253 Процедура не в состоянии обрабатывать XML-документ любой структуры.
254 В действительности файл cache из которого загружаются данные
255 просто напоминает XML с виду.
256 =cut
257 sub load_command_lines_from_xml
258 {
259 my $datafile = $_[0];
261 open (CLASS, $datafile)
262 or die "Can't open file with xml lablog ",$datafile,"\n";
263 local $/;
264 binmode CLASS, ":utf8";
265 $data = <CLASS>;
266 close(CLASS);
268 for $command ($data =~ m@<command>(.*?)</command>@sg) {
269 my %cl;
270 while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
271 $cl{$1} = $2;
272 }
273 push @Command_Lines, \%cl;
274 }
275 }
277 sub load_sessions_from_xml
278 {
279 my $datafile = $_[0];
281 open (CLASS, $datafile)
282 or die "Can't open file with xml lablog ",$datafile,"\n";
283 local $/;
284 binmode CLASS, ":utf8";
285 my $data = <CLASS>;
286 close(CLASS);
288 my $i=0;
289 for my $session ($data =~ m@<session>(.*?)</session>@msg) {
290 my %session_hash;
291 while ($session =~ m@<([^>]*?)>(.*?)</\1>@sg) {
292 $session_hash{$1} = $2;
293 }
294 $Sessions{$session_hash{local_session_id}} = \%session_hash;
295 }
296 }
299 # sort_command_lines
300 # In: @Command_Lines
301 # Out: @Command_Lies_Index
303 sub sort_command_lines
304 {
306 my @index;
307 for (my $i=0;$i<=$#Command_Lines;$i++) {
308 $index[$i]=$i;
309 }
311 @Command_Lines_Index = sort {
312 $Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
313 } @index;
315 }
317 ##################
318 # process_command_lines
319 #
320 # Обрабатываются командные строки @Command_Lines
321 # Для каждой строки определяется:
322 # class класс
323 # note комментарий
324 #
325 # In: @Command_Lines_Index
326 # In-Out: @Command_Lines
328 sub process_command_lines
329 {
331 COMMAND_LINE_PROCESSING:
332 for my $i (@Command_Lines_Index) {
333 my $cl = \$Command_Lines[$i];
335 next if !$cl;
337 for my $filter_key (keys %filter) {
338 next COMMAND_LINE_PROCESSING
339 if defined($$cl->{local_session_id})
340 && defined($Sessions{$$cl->{local_session_id}}->{$filter_key})
341 && $Sessions{$$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
342 }
344 $$cl->{id} = $$cl->{"time"};
346 $$cl->{err} ||=0;
348 # Класс команды
350 $$cl->{"class"} = $$cl->{"err"} eq 130 ? "interrupted"
351 : $$cl->{"err"} eq 127 ? "mistyped"
352 : $$cl->{"err"} ? "wrong"
353 : "normal";
355 if ($$cl->{"cline"} &&
356 $$cl->{"cline"} =~ /[^|`]\s*sudo/
357 || $$cl->{"uid"} eq 0) {
358 $$cl->{"class"}.="_root";
359 }
361 my $hint;
362 $hint = make_comment($$cl->{"cline"});
363 if ($hint) {
364 $$cl->{hint} = $hint;
365 }
366 # $$cl->{hint}="";
368 # Выводим <head_lines> верхних строк
369 # и <tail_lines> нижних строк,
370 # если эти параметры существуют
371 my $output="";
373 if ($$cl->{"last_command"} eq "cat" && !$$cl->{"err"} && !($$cl->{"cline"} =~ /</)) {
374 my $filename = $$cl->{"cline"};
375 $filename =~ s/.*\s+(\S+)\s*$/$1/;
376 $Files{$filename}->{"content"} = $$cl->{"output"};
377 $Files{$filename}->{"source_command_id"} = $$cl->{"id"}
378 }
379 my @lines = split '\n', $$cl->{"output"};
380 if ((
381 $Config{"head_lines"}
382 || $Config{"tail_lines"}
383 )
384 && $#lines > $Config{"head_lines"} + $Config{"tail_lines"} ) {
385 #
386 for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
387 $output .= $lines[$i]."\n";
388 }
389 $output .= $Config{"skip_text"}."\n";
391 my $start_line=$#lines-$Config{"tail_lines"}+1;
392 for (my $i=$start_line; $i<= $#lines; $i++) {
393 $output .= $lines[$i]."\n";
394 }
395 }
396 else {
397 $output = $$cl->{"output"};
398 }
399 $$cl->{short_output} = $output;
401 #Обработка пометок
402 # Если несколько пометок (notes) идут подряд,
403 # они все объединяются
405 if ($$cl->{cline} =~ /l3shot/) {
406 if ($$cl->{output} =~ m@Screenshot is written to.*/(.*)\.xwd@) {
407 $$cl->{screenshot}="$1";
408 }
409 }
411 if ($$cl->{cline}=~ m@cat[^#]*#([\^=v])\s*(.*)@) {
413 my $note_operator = $1;
414 my $note_title = $2;
416 if ($note_operator eq "=") {
417 $$cl->{"class"} = "note";
418 $$cl->{"note"} = $$cl->{"output"};
419 $$cl->{"note_title"} = $2;
420 }
421 else {
422 my $j = $i;
423 if ($note_operator eq "^") {
424 $j--;
425 $j-- while ($j >=0 && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
426 }
427 elsif ($note_operator eq "v") {
428 $j++;
429 $j++ while ($j <= @Command_Lines && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
430 }
431 $Command_Lines[$j]->{note_title}=$note_title;
432 $Command_Lines[$j]->{note}.=$$cl->{output};
433 $$cl=0;
434 }
435 }
436 elsif ($$cl->{cline}=~ /#([\^=v])(.*)/) {
438 my $note_operator = $1;
439 my $note_text = $2;
441 if ($note_operator eq "=") {
442 $$cl->{"class"} = "note";
443 $$cl->{"note"} = $note_text;
444 }
445 else {
446 my $j=$i;
447 if ($note_operator eq "^") {
448 $j--;
449 $j-- while ($j >=0 && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
450 }
451 elsif ($note_operator eq "v") {
452 $j++;
453 $j++ while ($j <= @Command_Lines && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
454 }
455 $Command_Lines[$j]->{note}.="$note_text\n";
456 $$cl=0;
457 }
458 }
459 if ($$cl->{"class"} eq "note") {
460 my $note_html = $$cl->{note};
461 $note_html = join ("\n", map ("<p>$_</p>", split (/-\n/, $note_html)));
462 $note_html =~ s@(http:[a-zA-Z.0-9/?\_%-]*)@<a href='$1'>$1</a>@g;
463 $note_html =~ s@(www\.[a-zA-Z.0-9/?\_%-]*)@<a href='$1'>$1</a>@g;
464 $$cl->{"note_html"} = $note_html;
465 }
466 }
468 }
471 =cut
472 Процедура print_command_lines выводит HTML-представление
473 разобранного lab-скрипта.
475 Разобранный lab-скрипт должен находиться в массиве @Command_Lines
476 =cut
478 sub print_command_lines_html
479 {
481 my @toc; # Оглавление
482 my $note_number=0;
484 my $result = q();
485 my $this_day_resut = q();
487 my $cl;
488 my $last_tty="";
489 my $last_session="";
490 my $last_day=q();
491 my $last_wday=q();
492 my $in_range=0;
494 my $current_command=0;
496 my @known_commands;
500 $Stat{LastCommand} ||= 0;
501 $Stat{TotalCommands} ||= 0;
502 $Stat{ErrorCommands} ||= 0;
503 $Stat{MistypedCommands} ||= 0;
505 my %new_entries_of = (
506 "1 1" => "программы пользователя",
507 "2 8" => "программы администратора",
508 "3 sh" => "команды интерпретатора",
509 "4 script"=> "скрипты",
510 );
512 COMMAND_LINE:
513 for my $k (@Command_Lines_Index) {
515 my $cl=$Command_Lines[$Command_Lines_Index[$current_command++]];
516 next unless $cl;
518 # Пропускаем команды, с одинаковым временем
519 # Это не совсем правильно.
520 # Возможно, что это команды, набираемые с помощью <completion>
521 # или запомненные с помощью <ctrl-c>
523 next if $Stat{LastCommand} == $cl->{time};
525 # Пропускаем строки, которые противоречат фильтру
526 # Если у нас недостаточно информации о том, подходит строка под фильтр или нет,
527 # мы её выводим
529 for my $filter_key (keys %filter) {
530 next COMMAND_LINE
531 if defined($cl->{local_session_id})
532 && defined($Sessions{$cl->{local_session_id}}->{$filter_key})
533 && $Sessions{$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
534 }
536 # Набираем статистику
537 # Хэш %Stat
539 $Stat{FirstCommand} = $cl->{time} unless $Stat{FirstCommand};
540 if ($cl->{time} - $Stat{LastCommand} < $Config{stat_inactivity_interval}) {
541 $Stat{TotalTime} += $cl->{time} - $Stat{LastCommand}
542 }
543 my $seconds_since_last_command = $cl->{time} - $Stat{LastCommand};
545 if ($Stat{LastCommand} > $cl->{time}) {
546 $result .= "Время идёт вспять<br/>";
547 };
548 $Stat{LastCommand} = $cl->{time};
549 $Stat{TotalCommands}++;
551 # Пропускаем строки, выходящие за границу "signature",
552 # при условии, что границы указаны
553 # Пропускаем неправильные/прерванные/другие команды
554 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
555 $in_range=1;
556 next;
557 }
558 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
559 $in_range=0;
560 next;
561 }
562 next if ($Config{"from"} && $Config{"to"} && !$in_range)
563 || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
564 || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
565 || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
570 #
571 ##
572 ## Начинается собственно вывод
573 ##
574 #
576 ### Сначала обрабатываем границы разделов
577 ### Если тип команды "note", это граница
579 if ($cl->{class} eq "note") {
580 $this_day_result .= "<tr><td colspan='6'>"
581 . "<h4 id='note$note_number'>".$cl->{note_title}."</h4>" if $cl->{note_title}
582 . "".$cl->{note_html}."<p/><p/></td></tr>";
584 if ($cl->{note_title}) {
585 push @{$toc[@toc]},"<a href='#note$note_number'>".$cl->{note_title}."</a>";
586 $note_number++;
587 }
588 next;
589 }
591 my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
593 # Добавляем спереди 0 для удобочитаемости
594 $min = "0".$min if $min =~ /^.$/;
595 $hour = "0".$hour if $hour =~ /^.$/;
596 $sec = "0".$sec if $sec =~ /^.$/;
598 $class=$cl->{"class"};
599 $Stat{ErrorCommands}++ if $class =~ /wrong/;
600 $Stat{MistypedCommands}++ if $class =~ /mistype/;
602 # DAY CHANGE
603 if ( $last_day ne $day) {
604 if ($last_day) {
606 # Вычисляем разность множеств.
607 # Что-то вроде этого, если бы так можно было писать:
608 # @new_commands = keys %frequency_of_command - @known_commands;
611 $result .= "<h3 id='day$last_day'>".$Day_Name[$last_wday]."</h3>";
612 for my $entry_class (sort keys %new_entries_of) {
613 my $table_caption = "Таблица ".$table_number++.".".$Day_Name[$last_wday]
614 .". Новые ".$new_entries_of{$entry_class};
615 my $new_commands_section = make_new_entries_table(
616 $table_caption,
617 $entry_class=~/[0-9]+\s+(.*)/,
618 \@known_commands);
619 }
620 @known_commands = keys %frequency_of_command;
621 $result .= $this_day_result;
622 }
624 push @toc, "<a href='#day$day'>".$Day_Name[$wday]."</a>\n";
625 $last_day=$day;
626 $last_wday=$wday;
627 $this_day_result = q();
628 }
629 else {
630 $this_day_result .= minutes_passed($seconds_since_last_command);
631 }
633 $this_day_result .= "<div class='command' id='command:".$cl->{"id"}."' >\n";
635 # CONSOLE CHANGE
636 if ($cl->{"tty"} && $last_tty ne $cl->{"tty"} && 0) {
637 my $tty = $cl->{"tty"};
638 $this_day_result .= "<div class='ttychange'>"
639 . $tty
640 ."</div>";
641 $last_tty=$cl->{"tty"};
642 }
644 # Session change
645 if ( $last_session ne $cl->{"local_session_id"}) {
646 my $tty;
647 if (defined $Sessions{$cl->{"local_session_id"}}->{"tty"}) {
648 $this_day_result .= "<div class='ttychange'><a href='?local_session_id=".$cl->{"local_session_id"}."'>"
649 . $Sessions{$cl->{"local_session_id"}}->{"tty"}
650 ."</a></div>";
651 }
652 $last_session=$cl->{"local_session_id"};
653 }
655 # TIME
656 if ($Config{"show_time"} =~ /^y/i) {
657 $this_day_result .= "<div class='time'>$hour:$min:$sec</div>"
658 }
660 # COMMAND
661 my $cline;
662 $prompt_hint = join (" ", map("$_=$cl->{$_}", grep (!/^(output|diff)$/, sort(keys(%{$cl})))));
663 $cline = "<span title='$prompt_hint'>".$cl->{"prompt"}."</span>".$cl->{"cline"};
664 $cline =~ s/\n//;
666 if ($cl->{"hint"}) {
667 $cline = "<span title='$cl->{hint}' class='with_hint'>$cline</span>" ;
668 }
669 else {
670 $cline = "<span class='without_hint'>$cline</span>";
671 }
673 $this_day_result .= "<table cellpadding='0' cellspacing='0'><tr><td>\n<div class='cblock_$cl->{class}'>\n";
674 $this_day_result .= "<div class='cline'>\n" . $cline ; #cline
675 $this_day_result .= "<span title='Код завершения ".$cl->{"err"}."'>\n"
676 . "<img src='".$Config{frontend_ico_path}."/error.png'/>\n"
677 . "</span>\n" if $cl->{"err"};
678 $this_day_result .= "</div>\n"; #cline
680 # OUTPUT
681 my $last_command = $cl->{"last_command"};
682 if (!(
683 $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
684 $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
685 $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
686 )) {
687 $this_day_result .= "<pre class='output'>\n" . $cl->{short_output} . "</pre>\n";
688 }
690 # DIFF
691 $this_day_result .= "<pre class='diff'>".$cl->{"diff"}."</pre>"
692 if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"});
693 # SHOT
694 $this_day_result .= "<img src='"
695 .$Config{l3shot_path}
696 .$cl->{"screenshot"}
697 .$Config{l3shot_suffix}
698 ."' alt ='screenshot id ".$cl->{"screenshot"}
699 ."'/>"
700 if ( $Config{"show_screenshots"} =~ /^y/i && $cl->{"screenshot"});
702 #NOTES
703 if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
704 my $note=$cl->{"note"};
705 $note =~ s/\n/<br\/>\n/msg;
706 if (not $note =~ s@(http:[a-zA-Z.0-9/_?%-]*)@<a href='$1'>$1</a>@g) {
707 $note =~ s@(www\.[a-zA-Z.0-9/_?%-]*)@<a href='$1'>$1</a>@g;
708 };
709 $this_day_result .= "<div class='note'>";
710 $this_day_result .= "<div class='note_title'>".$cl->{note_title}."</div>" if $cl->{note_title};
711 $this_day_result .= "<div class='note_text'>".$note."</div>";
712 $this_day_result .= "</div>\n";
713 }
715 # Вывод очередной команды окончен
716 $this_day_result .= "</div>\n"; # cblock
717 $this_day_result .= "</td></tr></table>\n"
718 . "</div>\n"; # command
719 }
720 last: {
721 $result .= "<h3 id='day$last_day'>".$Day_Name[$last_wday]."</h3>";
723 for my $entry_class (keys %new_entries_of) {
724 my $table_caption = "Таблица ".$table_number++.".".$Day_Name[$last_wday]
725 . ". Новые ".$new_entries_of{$entry_class};
726 my $new_commands_section = make_new_entries_table(
727 $table_caption,
728 $entry_class=~/[0-9]+\s+(.*)/,
729 \@known_commands);
730 }
731 @known_commands = keys %frequency_of_command;
732 $result .= $this_day_result;
733 }
735 return ($result, collapse_list (\@toc));
737 }
739 #############
740 # make_new_entries_table
741 #
742 # Напечатать таблицу неизвестных команд
743 #
744 # In: $_[0] table_caption
745 # $_[1] entries_class
746 # @_[2..] known_commands
747 # Out:
749 sub make_new_entries_table
750 {
751 my $table_caption;
752 my $entries_class = shift;
753 my @known_commands = @{$_[0]};
754 my $result = "";
756 my %count;
757 my @new_commands = ();
758 for my $c (keys %frequency_of_command, @known_commands) {
759 $count{$c}++
760 }
761 for my $c (keys %frequency_of_command) {
762 push @new_commands, $c if $count{$c} != 2;
763 }
765 my $new_commands_section;
766 if (@new_commands){
767 my $hint;
768 for my $c (reverse sort { $frequency_of_command{$a} <=> $frequency_of_command{$b} } @new_commands) {
769 $hint = make_comment($c);
770 next unless $hint;
771 my ($command, $hint) = $hint =~ m/(.*?) \s*- \s*(.*)/;
772 next unless $command =~ s/\($entries_class\)//i;
773 $new_commands_section .= "<tr><td valign='top'>$command</td><td>$hint</td></tr>";
774 }
775 }
776 if ($new_commands_section) {
777 $result .= "<table class='new_commands_table' width='700' cellspacing='0' cellpadding='0'>"
778 . "<tr class='new_commands_caption'>"
779 . "<td colspan='2' align='right'>$table_caption</td>"
780 . "</tr>"
781 . "<tr class='new_commands_header'>"
782 . "<td width=100>Команда</td><td width=600>Описание</td>"
783 . "</tr>"
784 . $new_commands_section
785 . "</table>"
786 }
787 return $result;
788 }
790 #############
791 # minutes_passed
792 #
793 #
794 #
795 # In: $_[0] seconds_since_last_command
796 # Out: "minutes passed" text
798 sub minutes_passed
799 {
800 my $seconds_since_last_command = shift;
801 my $result = "";
802 if ($seconds_since_last_command > 7200) {
803 my $hours_passed = int($seconds_since_last_command/3600);
804 my $passed_word = $hours_passed % 10 == 1 ? "прошла"
805 : "прошло";
806 my $hours_word = $hours_passed % 10 == 1 ? "часа":
807 "часов";
808 $result .= "<div class='much_time_passed'>"
809 . $passed_word." >".$hours_passed." ".$hours_word
810 . "</div>\n";
811 }
812 elsif ($seconds_since_last_command > 600) {
813 my $minutes_passed = int($seconds_since_last_command/60);
816 my $passed_word = $minutes_passed % 100 > 10
817 && $minutes_passed % 100 < 20 ? "прошло"
818 : $minutes_passed % 10 == 1 ? "прошла"
819 : "прошло";
821 my $minutes_word = $minutes_passed % 100 > 10
822 && $minutes_passed % 100 < 20 ? "минут" :
823 $minutes_passed % 10 == 1 ? "минута":
824 $minutes_passed % 10 == 0 ? "минут" :
825 $minutes_passed % 10 > 4 ? "минут" :
826 "минуты";
828 if ($seconds_since_last_command < 1800) {
829 $result .= "<div class='time_passed'>"
830 . $passed_word." ".$minutes_passed." ".$minutes_word
831 . "</div>\n";
832 }
833 else {
834 $result .= "<div class='much_time_passed'>"
835 . $passed_word." ".$minutes_passed." ".$minutes_word
836 . "</div>\n";
837 }
838 }
839 return $result;
840 }
842 #############
843 # print_all_txt
844 #
845 # Вывести журнал в текстовом формате
846 #
847 # In: $_[0] output_filename
848 # Out:
850 sub print_command_lines_txt
851 {
853 my $output_filename=$_[0];
854 my $note_number=0;
856 my $result = q();
857 my $this_day_resut = q();
859 my $cl;
860 my $last_tty="";
861 my $last_session="";
862 my $last_day=q();
863 my $last_wday=q();
864 my $in_range=0;
866 my $current_command=0;
868 my $cursor_position = 0;
871 if ($Config{filter}) {
872 # Инициализация фильтра
873 for (split /&/,$Config{filter}) {
874 my ($var, $val) = split /=/;
875 $filter{$var} = $val || "";
876 }
877 }
880 COMMAND_LINE:
881 for my $k (@Command_Lines_Index) {
883 my $cl=$Command_Lines[$Command_Lines_Index[$current_command++]];
884 next unless $cl;
887 # Пропускаем строки, которые противоречат фильтру
888 # Если у нас недостаточно информации о том, подходит строка под фильтр или нет,
889 # мы её выводим
891 for my $filter_key (keys %filter) {
892 next COMMAND_LINE
893 if defined($cl->{local_session_id})
894 && defined($Sessions{$cl->{local_session_id}}->{$filter_key})
895 && $Sessions{$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
896 }
898 # Пропускаем строки, выходящие за границу "signature",
899 # при условии, что границы указаны
900 # Пропускаем неправильные/прерванные/другие команды
901 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
902 $in_range=1;
903 next;
904 }
905 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
906 $in_range=0;
907 next;
908 }
909 next if ($Config{"from"} && $Config{"to"} && !$in_range)
910 || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
911 || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
912 || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
915 #
916 ##
917 ## Начинается собственно вывод
918 ##
919 #
921 ### Сначала обрабатываем границы разделов
922 ### Если тип команды "note", это граница
924 if ($cl->{class} eq "note") {
925 $this_day_result .= " === ".$cl->{note_title}." === \n" if $cl->{note_title};
926 $this_day_result .= $cl->{note}."\n";
927 next;
928 }
930 my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
932 # Добавляем спереди 0 для удобочитаемости
933 $min = "0".$min if $min =~ /^.$/;
934 $hour = "0".$hour if $hour =~ /^.$/;
935 $sec = "0".$sec if $sec =~ /^.$/;
937 $class=$cl->{"class"};
939 # DAY CHANGE
940 if ( $last_day ne $day) {
941 if ($last_day) {
942 $result .= "== ".$Day_Name[$last_wday]." == \n";
943 $result .= $this_day_result;
944 }
945 $last_day = $day;
946 $last_wday = $wday;
947 $this_day_result = q();
948 }
950 # CONSOLE CHANGE
951 if ($cl->{"tty"} && $last_tty ne $cl->{"tty"} && 0) {
952 my $tty = $cl->{"tty"};
953 $this_day_result .= " #l3: ------- другая консоль ----\n";
954 $last_tty=$cl->{"tty"};
955 }
957 # Session change
958 if ( $last_session ne $cl->{"local_session_id"}) {
959 $this_day_result .= "# ------------------------------------------------------------"
960 . " l3: local_session_id=".$cl->{"local_session_id"}
961 . " ---------------------------------- \n";
962 $last_session=$cl->{"local_session_id"};
963 }
965 # TIME
966 my @nl_counter = split (/\n/, $result);
967 $cursor_position=length($result) - @nl_counter;
969 if ($Config{"show_time"} =~ /^y/i) {
970 $this_day_result .= "$hour:$min:$sec"
971 }
973 # COMMAND
974 $this_day_result .= " ".$cl->{"prompt"}.$cl->{"cline"}."\n";
975 if ($cl->{"err"}) {
976 $this_day_result .= " #l3: err=".$cl->{'err'}."\n";
977 }
979 # OUTPUT
980 my $last_command = $cl->{"last_command"};
981 if (!(
982 $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
983 $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
984 $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
985 )) {
986 my $output = $cl->{short_output};
987 if ($output) {
988 $output =~ s/^/ |/mg;
989 }
990 $this_day_result .= $output;
991 }
993 # DIFF
994 if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
995 my $diff = $cl->{"diff"};
996 $diff =~ s/^/ |/mg;
997 $this_day_result .= $diff;
998 };
999 # SHOT
1000 if ($Config{"show_screenshots"} =~ /^y/i && $cl->{"screenshot"}) {
1001 $this_day_result .= " #l3: screenshot=".$cl->{'screenshot'}."\n";
1002 }
1004 #NOTES
1005 if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
1006 my $note=$cl->{"note"};
1007 $note =~ s/\n/\n#^/msg;
1008 $this_day_result .= "#^ == ".$cl->{note_title}." ==\n" if $cl->{note_title};
1009 $this_day_result .= "#^ ".$note."\n";
1010 }
1012 }
1013 last: {
1014 $result .= "== ".$Day_Name[$last_wday]." == \n";
1015 $result .= $this_day_result;
1016 }
1018 return $result;
1022 }
1024 #############
1025 # print_edit_all_html
1026 #
1027 # Вывести страницу с текстовым представлением журнала для редактирования
1028 #
1029 # In: $_[0] output_filename
1030 # Out:
1032 sub print_edit_all_html
1033 {
1034 my $output_filename= shift;
1035 my $result;
1036 my $cursor_position = 0;
1038 $result = print_command_lines_txt;
1039 my $title = ">Журнал лабораторных работ. Правка";
1041 $result =
1042 "<html>"
1043 ."<head>"
1044 ."<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />"
1045 ."<link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>"
1046 ."<title>$title</title>"
1047 ."</head>"
1048 ."<script>"
1049 .$SetCursorPosition_JS
1050 ."</script>"
1051 ."<body onLoad='setCursorPosition(document.all.mytextarea, $cursor_position, $cursor_position+10)'>"
1052 ."<h1>Журнал лабораторных работ. Правка</h1>"
1053 ."<form>"
1054 ."<textarea rows='30' cols='100' wrap='off' id='mytextarea'>$result</textarea>"
1055 ."<br/><input type='submit' value='Сохранить' label='label'/>"
1056 ."</form>"
1057 ."<p>Внимательно правим, потом сохраняем</p>"
1058 ."<p>Строки, начинающиеся символами #l3: можно трогать, только если точно знаешь, что делаешь</p>"
1059 ."</body>"
1060 ."</html>";
1062 if ($output_filename eq "-") {
1063 print $result;
1064 }
1065 else {
1066 open(OUT, ">", $output_filename)
1067 or die "Can't open $output_filename for writing\n";
1068 binmode ":utf8";
1069 print OUT "$result";
1070 close(OUT);
1071 }
1072 }
1074 #############
1075 # print_all_txt
1076 #
1077 # Вывести страницу с текстовым представлением журнала для редактирования
1078 #
1079 # In: $_[0] output_filename
1080 # Out:
1082 sub print_all_txt
1083 {
1084 my $result;
1086 $result = print_command_lines_txt;
1088 $result =~ s/>/>/g;
1089 $result =~ s/</</g;
1090 $result =~ s/&/&/g;
1092 if ($output_filename eq "-") {
1093 print $result;
1094 }
1095 else {
1096 open(OUT, ">:utf8", $output_filename)
1097 or die "Can't open $output_filename for writing\n";
1098 print OUT "$result";
1099 close(OUT);
1100 }
1101 }
1104 #############
1105 # print_all_html
1106 #
1107 #
1108 #
1109 # In: $_[0] output_filename
1110 # Out:
1113 sub print_all_html
1114 {
1115 my $output_filename=$_[0];
1117 my $result;
1118 my ($command_lines,$toc) = print_command_lines_html;
1119 my $files_section = print_files_html;
1121 $result = print_header_html($toc);
1124 # $result.= join " <br/>", keys %Sessions;
1125 # for my $sess (keys %Sessions) {
1126 # $result .= join " ", keys (%{$Sessions{$sess}});
1127 # $result .= "<br/>";
1128 # }
1130 $result.= "<h2 id='log'>Журнал</h2>" . $command_lines;
1131 $result.= "<h2 id='files'>Файлы</h2>" . $files_section if $files_section;
1132 $result.= "<h2 id='stat'>Статистика</h2>" . print_stat_html;
1133 $result.= "<h2 id='help'>Справка</h2>" . $Html_Help . "<br/>";
1134 $result.= "<h2 id='about'>О программе</h2>". $Html_About. "<br/>";
1135 $result.= print_footer_html;
1137 if ($output_filename eq "-") {
1138 binmode STDOUT, ":utf8";
1139 print $result;
1140 }
1141 else {
1142 open(OUT, ">:utf8", $output_filename)
1143 or die "Can't open $output_filename for writing\n";
1144 print OUT $result;
1145 close(OUT);
1146 }
1147 }
1149 #############
1150 # print_header_html
1151 #
1152 #
1153 #
1154 # In: $_[0] Содержание
1155 # Out: Распечатанный заголовок
1157 sub print_header_html
1158 {
1159 my $toc = $_[0];
1160 my $course_name = $Config{"course-name"};
1161 my $course_code = $Config{"course-code"};
1162 my $course_date = $Config{"course-date"};
1163 my $course_center = $Config{"course-center"};
1164 my $course_trainer = $Config{"course-trainer"};
1165 my $course_student = $Config{"course-student"};
1167 my $title = "Журнал лабораторных работ";
1168 $title .= " -- ".$course_student if $course_student;
1169 if ($course_date) {
1170 $title .= " -- ".$course_date;
1171 $title .= $course_code ? "/".$course_code
1172 : "";
1173 }
1174 else {
1175 $title .= " -- ".$course_code if $course_code;
1176 }
1178 # Управляющая форма
1179 my $control_form .= "<div class='visibility_form' title='Выберите какие элементы должны быть показаны в журнале'>"
1180 . "<span class='header'>Видимые элементы</span>"
1181 . "<span class='window_controls'><a href='' onclick='' title='свернуть форму управления'>_</a> <a href='' onclick='' title='закрыть форму управления'>x</a></span>"
1182 . "<div><form>\n";
1183 for my $element (sort keys %Elements_Visibility)
1184 {
1185 my ($skip, @e) = split /\s+/, $element;
1186 my $showhide = join "", map { "ShowHide('$_');" } @e ;
1187 $control_form .= "<div><input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
1188 $Elements_Visibility{$element}.
1189 "</input></div>";
1190 }
1191 $control_form .= "</form>\n"
1192 . "</div>\n";
1195 # Управляющая форма отключена
1196 # Она слишеком сильно мешает, нужно что-то переделать
1197 $control_form = "";
1199 my $result;
1200 $result = <<HEADER;
1201 <html>
1202 <head>
1203 <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
1204 <link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>
1205 <title>$title</title>
1206 </head>
1207 <body>
1208 <script>
1209 $Html_JavaScript
1210 </script>
1212 <!-- vvv Tigra Hints vvv -->
1213 <script language="JavaScript" src="/tigra/hints.js"></script>
1214 <script language="JavaScript" src="/tigra/hints_cfg.js"></script>
1215 <style>
1216 /* a class for all Tigra Hints boxes, TD object */
1217 .hintsClass
1218 {text-align: center; font-family: Verdana, Arial, Helvetica; padding: 0px 0px 0px 0px;}
1219 /* this class is used by Tigra Hints wrappers */
1220 .row
1221 {background: white;}
1222 </style>
1223 <!-- ^^^ Tigra Hints ^^^ -->
1226 <div class='edit_link'>
1227 [ <a href='?action=edit&$filter_url'>править</a> ]
1228 </div>
1229 <h1 onmouseover="myHint.show('1')" onmouseout="myHint.hide()" class='lined_header'>Журнал лабораторных работ</h1>
1230 HEADER
1231 if ( $course_student
1232 || $course_trainer
1233 || $course_name
1234 || $course_code
1235 || $course_date
1236 || $course_center) {
1237 $result .= "<p>";
1238 $result .= "Выполнил $course_student<br/>" if $course_student;
1239 $result .= "Проверил $course_trainer <br/>" if $course_trainer;
1240 $result .= "Курс " if $course_name
1241 || $course_code
1242 || $course_date;
1243 $result .= "$course_name " if $course_name;
1244 $result .= "($course_code)" if $course_code;
1245 $result .= ", $course_date<br/>" if $course_date;
1246 $result .= "Учебный центр $course_center <br/>" if $course_center;
1247 $result .= "Фильтр ".join(" ", map("$filter{$_}=$_", keys %filter))."<br/>" if %filter;
1248 $result .= "</p>";
1249 }
1251 $result .= <<HEADER;
1252 <table width='100%'>
1253 <tr>
1254 <td width='*'>
1256 <table border=0 id='toc' class='toc'>
1257 <tr>
1258 <td>
1259 <div class='toc_title'>Содержание</div>
1260 <ul>
1261 <li><a href='#log'>Журнал</a></li>
1262 <ul>$toc</ul>
1263 <li><a href='#files'>Файлы</a></li>
1264 <li><a href='#stat'>Статистика</a></li>
1265 <li><a href='#help'>Справка</a></li>
1266 <li><a href='#about'>О программе</a></li>
1267 </ul>
1268 </td>
1269 </tr>
1270 </table>
1272 </td>
1273 <td valign='top' width=200>$control_form</td>
1274 </tr>
1275 </table>
1276 HEADER
1278 return $result;
1279 }
1282 #############
1283 # print_footer_html
1284 #
1285 #
1286 #
1287 #
1288 #
1290 sub print_footer_html
1291 {
1292 return "</body>\n</html>\n";
1293 }
1298 #############
1299 # print_stat_html
1300 #
1301 #
1302 #
1303 # In:
1304 # Out:
1306 sub print_stat_html
1307 {
1308 %StatNames = (
1309 FirstCommand => "Время первой команды журнала",
1310 LastCommand => "Время последней команды журнала",
1311 TotalCommands => "Количество командных строк в журнале",
1312 ErrorsPercentage => "Процент команд с ненулевым кодом завершения, %",
1313 MistypesPercentage => "Процент синтаксически неверно набранных команд, %",
1314 TotalTime => "Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час",
1315 CommandsPerTime => "Количество командных строк в единицу времени, команда/мин",
1316 CommandsFrequency => "Частота использования команд",
1317 RareCommands => "Частота использования этих команд < 0.5%",
1318 );
1319 @StatOrder = (
1320 FirstCommand,
1321 LastCommand,
1322 TotalCommands,
1323 ErrorsPercentage,
1324 MistypesPercentage,
1325 TotalTime,
1326 CommandsPerTime,
1327 CommandsFrequency,
1328 RareCommands,
1329 );
1331 # Подготовка статистики к выводу
1332 # Некоторые значения пересчитываются!
1333 # Дальше их лучше уже не использовать!!!
1335 my %CommandsFrequency = %frequency_of_command;
1337 $Stat{TotalTime} ||= 0;
1338 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{FirstCommand} || 0);
1339 $Stat{FirstCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec, $year+1900, $mon+1, $mday;
1340 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{LastCommand} || 0);
1341 $Stat{LastCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec, $year+1900, $mon+1, $mday;
1342 if ($Stat{TotalCommands}) {
1343 $Stat{ErrorsPercentage} = sprintf "%5.2f", $Stat{ErrorCommands}*100/$Stat{TotalCommands};
1344 $Stat{MistypesPercentage} = sprintf "%5.2f", $Stat{MistypedCommands}*100/$Stat{TotalCommands};
1345 }
1346 $Stat{CommandsPerTime} = sprintf "%5.2f", $Stat{TotalCommands}*60/$Stat{TotalTime}
1347 if $Stat{TotalTime};
1348 $Stat{TotalTime} = sprintf "%5.2f", $Stat{TotalTime}/60/60;
1350 my $total_commands=0;
1351 for $command (keys %CommandsFrequency){
1352 $total_commands += $CommandsFrequency{$command};
1353 }
1354 if ($total_commands) {
1355 for $command (reverse sort {$CommandsFrequency{$a} <=> $CommandsFrequency{$b}} keys %CommandsFrequency){
1356 my $command_html;
1357 my $percentage = sprintf "%5.2f",$CommandsFrequency{$command}*100/$total_commands;
1358 if ($percentage < 0.5) {
1359 my $hint = make_comment($command);
1360 $command_html = "$command";
1361 $command_html = "<span title='$hint' class='with_hint'>$command_html</span>" if $hint;
1362 $command_html = "<span class='without_hint'>$command_html</span>" if not $hint;
1363 my $command_html = "<tt>$command_html</tt>";
1364 $Stat{RareCommands} .= $command_html."<sub><font size='-2'>".$CommandsFrequency{$command}."</font></sub> , ";
1365 }
1366 else {
1367 my $hint = make_comment($command);
1368 $command_html = "$command";
1369 $command_html = "<span title='$hint' class='with_hint'>$command_html</span>" if $hint;
1370 $command_html = "<span class='without_hint'>$command_html</span>" if not $hint;
1371 my $command_html = "<tt>$command_html</tt>";
1372 $percentage = sprintf "%5.2f",$percentage;
1373 $Stat{CommandsFrequency} .= "<tr><td>".$command_html."</td><td>".$CommandsFrequency{$command}."</td>".
1374 "<td>|".("="x int($CommandsFrequency{$command}*100/$total_commands))."| $percentage%</td></tr>";
1375 }
1376 }
1377 $Stat{CommandsFrequency} = "<table>".$Stat{CommandsFrequency}."</table>";
1378 $Stat{RareCommands} =~ s/, $// if $Stat{RareCommands};
1379 }
1381 my $result = q();
1382 for my $stat (@StatOrder) {
1383 next unless $Stat{"$stat"};
1384 $result .= "<tr valign='top'><td width='300'>".$StatNames{"$stat"}."</td><td>".$Stat{"$stat"}."</td></tr>"
1385 }
1386 $result = "<table>$result</table>"
1387 . "<font size='-2'>____<br/>*) Интервалы неактивности длительностью "
1388 . ($Config{stat_inactivity_interval}/60)
1389 . " минут и более не учитываются</font></br>";
1391 return $result;
1392 }
1395 sub collapse_list($)
1396 {
1397 my $res = "";
1398 for my $elem (@{$_[0]}) {
1399 if (ref $elem eq "ARRAY") {
1400 $res .= "<ul>".collapse_list($elem)."</ul>";
1401 }
1402 else
1403 {
1404 $res .= "<li>".$elem."</li>";
1405 }
1406 }
1407 return $res;
1408 }
1411 sub print_files_html
1412 {
1413 my $result = qq();
1414 my @toc;
1415 for my $file (sort keys %Files) {
1416 my $div_id = "file:$file";
1417 $div_id =~ s@/@_@g;
1418 push @toc, "<a href='#$div_id'>$file</a>";
1419 $result .= "<div class='filename' id='$div_id'>".$file."</div>\n"
1420 . "<div class='file_navigation'><a href='#command:".$Files{$file}->{source_command_id}."'>".">"."</a></div>"
1421 . "<div class='filedata'><pre>".$Files{$file}->{content}."</pre></div>";
1422 }
1423 if ($result) {
1424 return "<div class='files_toc'>".collapse_list(\@toc)."</div>".$result;
1425 }
1426 else {
1427 return "";
1428 }
1429 }
1432 sub init_variables
1433 {
1434 $Html_Help = <<HELP;
1435 Для того чтобы использовать LiLaLo, не нужно знать ничего особенного:
1436 всё происходит само собой.
1437 Однако, чтобы ведение и последующее использование журналов
1438 было как можно более эффективным, желательно иметь в виду следующее:
1439 <ol>
1440 <li><p>
1441 В журнал автоматически попадают все команды, данные в любом терминале системы.
1442 </p></li>
1443 <li><p>
1444 Для того чтобы убедиться, что журнал на текущем терминале ведётся,
1445 и команды записываются, дайте команду w.
1446 В поле WHAT, соответствующем текущему терминалу,
1447 должна быть указана программа script.
1448 </p></li>
1449 <li><p>
1450 Команды, при наборе которых были допущены синтаксические ошибки,
1451 выводятся перечёркнутым текстом:
1452 <table>
1453 <tr class='command'>
1454 <td class='script'>
1455 <pre class='_mistyped_cline'>
1456 \$ l s-l</pre>
1457 <pre class='_mistyped_output'>bash: l: command not found
1458 </pre>
1459 </td>
1460 </tr>
1461 </table>
1462 <br/>
1463 </p></li>
1464 <li><p>
1465 Если код завершения команды равен нулю,
1466 команда была выполнена без ошибок.
1467 Команды, код завершения которых отличен от нуля, выделяются цветом.
1468 <table>
1469 <tr class='command'>
1470 <td class='script'>
1471 <pre class='_wrong_cline'>
1472 \$ test 5 -lt 4</pre>
1473 </pre>
1474 </td>
1475 </tr>
1476 </table>
1477 Обратите внимание на то, что код завершения команды может быть отличен от нуля
1478 не только в тех случаях, когда команда была выполнена с ошибкой.
1479 Многие команды используют код завершения, например, для того чтобы показать результаты проверки
1480 <br/>
1481 </p></li>
1482 <li><p>
1483 Команды, ход выполнения которых был прерван пользователем, выделяются цветом.
1484 <table>
1485 <tr class='command'>
1486 <td class='script'>
1487 <pre class='_interrupted_cline'>
1488 \$ find / -name abc</pre>
1489 <pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung
1490 find: /home/devi-orig/.gnome2_private: Keine Berechtigung
1491 find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung
1492 find: /home/devi-orig/.metacity: Keine Berechtigung
1493 find: /home/devi-orig/.inkscape: Keine Berechtigung
1494 ^C
1495 </pre>
1496 </td>
1497 </tr>
1498 </table>
1499 <br/>
1500 </p></li>
1501 <li><p>
1502 Команды, выполненные с привилегиями суперпользователя,
1503 выделяются слева красной чертой.
1504 <table>
1505 <tr class='command'>
1506 <td class='script'>
1507 <pre class='_root_cline'>
1508 # id</pre>
1509 <pre class='_root_output'>
1510 uid=0(root) gid=0(root) Gruppen=0(root)
1511 </pre>
1512 </td>
1513 </tr>
1514 </table>
1515 <br/>
1516 </p></li>
1517 <li><p>
1518 Изменения, внесённые в текстовый файл с помощью редактора,
1519 запоминаются и показываются в журнале в формате ed.
1520 Строки, начинающиеся символом "<", удалены, а строки,
1521 начинающиеся символом ">" -- добавлены.
1522 <table>
1523 <tr class='command'>
1524 <td class='script'>
1525 <pre class='cline'>
1526 \$ vi ~/.bashrc</pre>
1527 <table><tr><td width='5'/><td class='diff'><pre>2a3,5
1528 > if [ -f /usr/local/etc/bash_completion ]; then
1529 > . /usr/local/etc/bash_completion
1530 > fi
1531 </pre></td></tr></table></td>
1532 </tr>
1533 </table>
1534 <br/>
1535 </p></li>
1536 <li><p>
1537 Для того чтобы изменить файл в соответствии с показанными в диффшоте
1538 изменениями, можно воспользоваться командой patch.
1539 Нужно скопировать изменения, запустить программу patch, указав в
1540 качестве её аргумента файл, к которому применяются изменения,
1541 и всавить скопированный текст:
1542 <table>
1543 <tr class='command'>
1544 <td class='script'>
1545 <pre class='cline'>
1546 \$ patch ~/.bashrc</pre>
1547 </td>
1548 </tr>
1549 </table>
1550 В данном случае изменения применяются к файлу ~/.bashrc
1551 </p></li>
1552 <li><p>
1553 Для того чтобы получить краткую справочную информацию о команде,
1554 нужно подвести к ней мышь. Во всплывающей подсказке появится краткое
1555 описание команды.
1556 </p>
1557 <p>
1558 Если справочная информация о команде есть,
1559 команда выделяется голубым фоном, например: <span class="with_hint" title="главный текстовый редактор Unix">vi</span>.
1560 Если справочная информация отсутствует,
1561 команда выделяется розовым фоном, например: <span class="without_hint">notepad.exe</span>.
1562 Справочная информация может отсутствовать в том случае,
1563 если (1) команда введена неверно; (2) если распознавание команды LiLaLo выполнено неверно;
1564 (3) если информация о команде неизвестна LiLaLo.
1565 Последнее возможно для редких команд.
1566 </p></li>
1567 <li><p>
1568 Большие, в особенности многострочные, всплывающие подсказки лучше
1569 всего показываются браузерами KDE Konqueror, Apple Safari и Microsoft Internet Explorer.
1570 В браузерах Mozilla и Firefox они отображаются не полностью,
1571 а вместо перевода строки выводится специальный символ.
1572 </p></li>
1573 <li><p>
1574 Время ввода команды, показанное в журнале, соответствует времени
1575 <i>начала ввода командной строки</i>, которое равно тому моменту,
1576 когда на терминале появилось приглашение интерпретатора
1577 </p></li>
1578 <li><p>
1579 Имя терминала, на котором была введена команда, показано в специальном блоке.
1580 Этот блок показывается только в том случае, если терминал
1581 текущей команды отличается от терминала предыдущей.
1582 </p></li>
1583 <li><p>
1584 Вывод не интересующих вас в настоящий момент элементов журнала,
1585 таких как время, имя терминала и других, можно отключить.
1586 Для этого нужно воспользоваться <a href='#visibility_form'>формой управления журналом</a>
1587 вверху страницы.
1588 </p></li>
1589 <li><p>
1590 Небольшие комментарии к командам можно вставлять прямо из командной строки.
1591 Комментарий вводится прямо в командную строку, после символов #^ или #v.
1592 Символы ^ и v показывают направление выбора команды, к которой относится комментарий:
1593 ^ - к предыдущей, v - к следующей.
1594 Например, если в командной строке было введено:
1595 <pre class='cline'>
1596 \$ whoami
1597 </pre>
1598 <pre class='output'>
1599 user
1600 </pre>
1601 <pre class='cline'>
1602 \$ #^ Интересно, кто я?
1603 </pre>
1604 в журнале это будет выглядеть так:
1606 <pre class='cline'>
1607 \$ whoami
1608 </pre>
1609 <pre class='output'>
1610 user
1611 </pre>
1612 <table class='note'><tr><td width='100%' class='note_text'>
1613 <tr> <td> Интересно, кто я?<br/> </td></tr></table>
1614 </p></li>
1615 <li><p>
1616 Если комментарий содержит несколько строк,
1617 его можно вставить в журнал следующим образом:
1618 <pre class='cline'>
1619 \$ whoami
1620 </pre>
1621 <pre class='output'>
1622 user
1623 </pre>
1624 <pre class='cline'>
1625 \$ cat > /dev/null #^ Интересно, кто я?
1626 </pre>
1627 <pre class='output'>
1628 Программа whoami выводит имя пользователя, под которым
1629 мы зарегистрировались в системе.
1630 -
1631 Она не может ответить на вопрос о нашем назначении
1632 в этом мире.
1633 </pre>
1634 В журнале это будет выглядеть так:
1635 <table>
1636 <tr class='command'>
1637 <td class='script'>
1638 <pre class='cline'>
1639 \$ whoami</pre>
1640 <pre class='output'>user
1641 </pre>
1642 <table class='note'><tr><td class='note_title'>Интересно, кто я?</td></tr><tr><td width='100%' class='note_text'>
1643 Программа whoami выводит имя пользователя, под которым<br/>
1644 мы зарегистрировались в системе.<br/>
1645 <br/>
1646 Она не может ответить на вопрос о нашем назначении<br/>
1647 в этом мире.<br/>
1648 </td></tr></table>
1649 </td>
1650 </tr>
1651 </table>
1652 Для разделения нескольких абзацев между собой
1653 используйте символ "-", один в строке.
1654 <br/>
1655 </p></li>
1656 <li><p>
1657 Комментарии, не относящиеся непосредственно ни к какой из команд,
1658 добавляются точно таким же способом, только вместо симолов #^ или #v
1659 нужно использовать символы #=
1660 </p></li>
1662 <p><li>
1663 Содержимое файла может быть показано в журнале.
1664 Для этого его нужно вывести с помощью программы cat.
1665 Если вывод команды отметить симоволами #!,
1666 содержимое файла будет показано в журнале
1667 в специально отведённой для этого секции.
1668 </li></p>
1670 <p>
1671 <li>
1672 Для того чтобы вставить скриншот интересующего вас окна в журнал,
1673 нужно воспользоваться командой l3shot.
1674 После того как команда вызвана, нужно с помощью мыши выбрать окно, которое
1675 должно быть в журнале.
1676 </li>
1677 </p>
1679 <p>
1680 <li>
1681 Команды в журнале расположены в хронологическом порядке.
1682 Если две команды давались одна за другой, но на разных терминалах,
1683 в журнале они будут рядом, даже если они не имеют друг к другу никакого отношения.
1684 <pre>
1685 1
1686 2
1687 3
1688 4
1689 </pre>
1690 Группы команд, выполненных на разных терминалах, разделяются специальной линией.
1691 Под этой линией в правом углу показано имя терминала, на котором выполнялись команды.
1692 Для того чтобы посмотреть команды только одного сенса,
1693 нужно щёкнуть по этому названию.
1694 </li>
1695 </p>
1696 </ol>
1697 HELP
1699 $Html_About = <<ABOUT;
1700 <p>
1701 LiLaLo (L3) расшифровывается как Live Lab Log.<br/>
1702 Программа разработана для повышения эффективности обучения Unix/Linux-системам.<br/>
1703 (c) Игорь Чубин, 2004-2006<br/>
1704 </p>
1705 ABOUT
1706 $Html_About.='$Id$ </p>';
1708 $Html_JavaScript = <<JS;
1709 function getElementsByClassName(Class_Name)
1710 {
1711 var Result=new Array();
1712 var All_Elements=document.all || document.getElementsByTagName('*');
1713 for (i=0; i<All_Elements.length; i++)
1714 if (All_Elements[i].className==Class_Name)
1715 Result.push(All_Elements[i]);
1716 return Result;
1717 }
1718 function ShowHide (name)
1719 {
1720 elements=getElementsByClassName(name);
1721 for(i=0; i<elements.length; i++)
1722 if (elements[i].style.display == "none")
1723 elements[i].style.display = "";
1724 else
1725 elements[i].style.display = "none";
1726 //if (elements[i].style.visibility == "hidden")
1727 // elements[i].style.visibility = "visible";
1728 //else
1729 // elements[i].style.visibility = "hidden";
1730 }
1731 function filter_by_output(text)
1732 {
1734 var jjj=0;
1736 elements=getElementsByClassName('command');
1737 for(i=0; i<elements.length; i++) {
1738 subelems = elements[i].getElementsByTagName('pre');
1739 for(j=0; j<subelems.length; j++) {
1740 if (subelems[j].className = 'output') {
1741 var str = new String(subelems[j].nodeValue);
1742 if (jjj != 1) {
1743 alert(str);
1744 jjj=1;
1745 }
1746 if (str.indexOf(text) >0)
1747 subelems[j].style.display = "none";
1748 else
1749 subelems[j].style.display = "";
1751 }
1753 }
1754 }
1756 }
1757 JS
1759 $SetCursorPosition_JS = <<JS;
1760 function setCursorPosition(oInput,oStart,oEnd) {
1761 oInput.focus();
1762 if( oInput.setSelectionRange ) {
1763 oInput.setSelectionRange(oStart,oEnd);
1764 } else if( oInput.createTextRange ) {
1765 var range = oInput.createTextRange();
1766 range.collapse(true);
1767 range.moveEnd('character',oEnd);
1768 range.moveStart('character',oStart);
1769 range.select();
1770 }
1771 }
1772 JS
1774 %Search_Machines = (
1775 "google" => { "query" => "http://www.google.com/search?q=" ,
1776 "icon" => "$Config{frontend_google_ico}" },
1777 "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
1778 "icon" => "$Config{frontend_freebsd_ico}" },
1779 "linux" => { "query" => "http://man.he.net/?topic=",
1780 "icon" => "$Config{frontend_linux_ico}"},
1781 "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=",
1782 "icon" => "$Config{frontend_opennet_ico}"},
1783 "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
1784 "icon" => "$Config{frontend_local_ico}" },
1786 );
1788 %Elements_Visibility = (
1789 "0 new_commands_table" => "новые команды",
1790 "1 diff" => "редактор",
1791 "2 time" => "время",
1792 "3 ttychange" => "терминал",
1793 "4 wrong_output wrong_cline wrong_root_output wrong_root_cline"
1794 => "команды с ненулевым кодом завершения",
1795 "5 mistyped_output mistyped_cline mistyped_root_output mistyped_root_cline"
1796 => "неверно набранные команды",
1797 "6 interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline"
1798 => "прерванные команды",
1799 "7 tab_completion_output tab_completion_cline"
1800 => "продолжение с помощью tab"
1801 );
1803 @Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
1804 @Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
1805 @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
1806 }
1811 # Временно удалённый код
1812 # Возможно, он не понадобится уже никогда
1815 sub search_by
1816 {
1817 my $sm = shift;
1818 my $topic = shift;
1819 $topic =~ s/ /+/;
1821 return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
1822 $Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
1823 }