lilalo

view l3-frontend @ 150:822b36252d7f

Вывод больших фрагментов текста не теряется.

Большие фрагменты текста теперь не вырезаются бесследно.
Там, откуда они вырезаются, вставляются ссылки,
по которым можно посмотреть полную версию вывода.
Испытано на больших фрагментах текста,
содержащих до 5000 строк (фрагменты более 5000 строк по умолчанию
обрезаются административно; допустимые размеры задаются в l3config.pm).
Исправлены ошибки, из-за которых большие фрагменты
обрабатывались некорректно.
author igor@chub.in
date Tue Jun 23 01:15:02 2009 +0300 (2009-06-23)
parents 266dae9ce2a1
children 80691b40e6db
line source
1 #!/usr/bin/perl
3 use POSIX qw(strftime);
4 use lib '/etc/lilalo';
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 %Sessions;
13 our %Uploads;
15 our $debug_output=""; # Используйте эту переменную, если нужно передать отладочную информацию
17 our %filter;
18 our $filter_url;
19 sub init_filter;
21 our %Files;
23 # vvv Инициализация переменных выполняется процедурой init_variables
24 our @Day_Name;
25 our @Month_Name;
26 our @Of_Month_Name;
27 our %Search_Machines;
28 our %Elements_Visibility;
29 # ^^^
31 our $First_Command=$0;
32 our $Last_Command=40;
34 our %Stat;
35 our %frequency_of_command; # Сколько раз в журнале встречается какая команда
36 our $table_number=1;
37 our %tigra_hints;
39 my %mywi_cache_for; # Кэш для экономии обращений к mywi
41 sub count_frequency_of_commands;
42 sub make_comment;
43 sub make_new_entries_table;
44 sub load_command_lines_from_xml;
45 sub load_sessions_from_xml;
46 sub load_uploads;
47 sub sort_command_lines;
48 sub process_command_lines;
49 sub init_variables;
50 sub main;
51 sub collapse_list($);
53 sub minutes_passed;
55 sub print_all_txt;
56 sub print_all_html;
57 sub print_edit_all_html;
58 sub print_command_lines_html;
59 sub print_command_lines_txt;
60 sub print_files_html;
61 sub print_stat_html;
62 sub print_header_html;
63 sub print_footer_html;
64 sub tigra_hints_generate;
67 #### mywi
68 #
69 sub mywi_init;
70 sub load_mywitxt;
71 sub mywi_process_query($);
72 #
73 sub add_to_log($$);
74 sub parse_query;
75 sub search_in_txt;
76 sub add_to_log($$);
77 sub mywi_guess($);
78 #
80 main();
82 sub main
83 {
84 $| = 1;
86 init_variables();
87 init_config();
88 $Config{frontend_ico_path}=$Config{frontend_css};
89 $Config{frontend_ico_path}=~s@/[^/]*$@@;
90 init_filter();
91 mywi_init();
93 load_command_lines_from_xml($Config{"backend_datafile"});
94 load_uploads($Config{"upload_dir"});
95 load_sessions_from_xml($Config{"backend_datafile"});
96 sort_command_lines;
97 process_command_lines;
98 if (defined($filter{action}) && $filter{action} eq "edit") {
99 print_edit_all_html($Config{"output"});
100 }
101 else {
102 print_all_html($Config{"output"});
103 }
104 }
106 sub init_filter
107 {
108 if ($Config{filter}) {
109 # Инициализация фильтра
110 for (split /;;/,$Config{filter}) {
111 my ($var, $val) = split /::/;
112 $filter{$var} = $val || "";
113 }
114 }
115 $filter_url = join (";;", map("$_::$filter{$_}", keys %filter));
116 }
118 # extract_from_cline
120 # In: $what = commands | args
121 # Out: return ссылка на хэш, содержащий результаты разбора
122 # команда => позиция
124 # Разобрать командную строку $_[1] и возвратить хэш, содержащий
125 # номер первого появление команды в строке:
126 # команда => первая позиция
127 sub extract_from_cline
128 {
129 my $what = $_[0];
130 my $cline = $_[1];
131 my @lists = split /\;/, $cline;
134 my @command_lines = ();
135 for my $command_list (@lists) {
136 push(@command_lines, split(/\|/, $command_list));
137 }
139 my %position_of_command;
140 my %position_of_arg;
141 my $i=0;
142 for my $command_line (@command_lines) {
143 $command_line =~ s@^\s*@@;
144 $command_line =~ /\s*(\S+)\s*(.*)/;
145 if ($1 && $1 eq "sudo" ) {
146 $position_of_command{"$1"}=$i++;
147 $command_line =~ s/\s*sudo\s+//;
148 }
149 if ($command_line !~ m@^\s*\S*/etc/@) {
150 $command_line =~ s@^\s*\S+/@@;
151 }
153 $command_line =~ /\s*(\S+)\s*(.*)/;
154 my $command = $1;
155 my $args = $2;
156 if ($command && !defined $position_of_command{"$command"}) {
157 $position_of_command{"$command"}=$i++;
158 };
159 if ($args) {
160 my @args = split (/\s+/, $args);
161 for my $a (@args) {
162 $position_of_arg{"$a"}=$i++
163 if !defined $position_of_arg{"$a"};
164 };
165 }
166 }
168 if ($what eq "commands") {
169 return \%position_of_command;
170 } else {
171 return \%position_of_arg;
172 }
174 }
176 sub mywrap($)
177 {
178 return '<div class="t"><div class="b"><div class="l"><div class="r"><div class="bl"><div class="br"><div class="tl"><div class="tr">'.$_[0].
179 '</div></div></div></div></div></div></div></div>';
180 }
182 sub tigra_hints_generate
183 {
184 my $tigra_hints_items="";
185 for my $hint_id (keys %tigra_hints) {
186 $tigra_hints{$hint_id} =~ s@\n@<br/>@gs;
187 $tigra_hints{$hint_id} =~ s@ - @ — @gs;
188 $tigra_hints{$hint_id} =~ s@'@\\'@gs;
189 # $tigra_hints_items .= "'$hint_id' : mywrap('".$tigra_hints{$hint_id}."'),";
190 $tigra_hints_items .= "'$hint_id' : '".mywrap($tigra_hints{$hint_id})."',";
191 }
192 $tigra_hints_items =~ s/,$//;
193 return <<TIGRA;
195 var HINTS_CFG = {
196 'top' : 5, // a vertical offset of a hint from mouse pointer
197 'left' : 5, // a horizontal offset of a hint from mouse pointer
198 'css' : 'hintsClass', // a style class name for all hints, TD object
199 'show_delay' : 500, // a delay between object mouseover and hint appearing
200 'hide_delay' : 2000, // a delay between hint appearing and hint hiding
201 'wise' : true,
202 'follow' : true,
203 'z-index' : 0 // a z-index for all hint layers
204 },
206 HINTS_CFG_NEW = {
207 'wise' : true, // don't go off screen, don't overlap the object in the document
208 'margin' : 10, // minimum allowed distance between the hint and the window edge (negative values accepted)
209 'gap' : 20, // minimum allowed distance between the hint and the origin (negative values accepted)
210 'align' : 'bctl', // align of the hint and the origin (by first letters origin's top|middle|bottom left|center|right to hint's top|middle|bottom left|center|right)
211 'css' : 'hintsClass', // a style class name for all hints, applied to DIV element (see style section in the header of the document)
212 'show_delay' : 0, // a delay between initiating event (mouseover for example) and hint appearing
213 'hide_delay' : 200, // a delay between closing event (mouseout for example) and hint disappearing
214 'follow' : true, // hint follows the mouse as it moves
215 'z-index' : 100, // a z-index for all hint layers
216 'IEfix' : false, // fix IE problem with windowed controls visible through hints (activate if select boxes are visible through the hints)
217 'IEtrans' : ['blendTrans(DURATION=.3)', null], // [show transition, hide transition] - nice transition effects, only work in IE5+
218 'opacity' : 90 // opacity of the hint in %%
219 },
221 HINTS_ITEMS = {
222 $tigra_hints_items
223 };
224 var myHint = new THints (HINTS_CFG, HINTS_ITEMS);
227 function mywrap (s_) {
228 return '<div class="t"><div class="b"><div class="l"><div class="r"><div class="bl"><div class="br"><div class="tl"><div class="tr">'+s_+
229 '</div></div></div></div></div></div></div></div>';
231 }
232 TIGRA
233 $a=<<TIGRA;
234 TIGRA
235 }
238 sub count_frequency_of_commands
239 {
240 my $cline = $_[0];
241 my @commands = keys %{extract_from_cline("commands", $cline)};
242 for my $command (@commands) {
243 $frequency_of_command{$command}++;
244 }
245 }
247 sub make_comment
248 {
249 my $cline = $_[0];
250 #my $files = $_[1];
252 my @comments;
253 my @commands = keys %{extract_from_cline("commands", $cline)};
254 my @args = keys %{extract_from_cline("args", $cline)};
255 return if (!@commands && !@args);
256 #return "commands=".join(" ",@commands)."; files=".join(" ",@files);
258 # Commands
259 for my $command (@commands) {
260 $command =~ s/'//g;
261 #$frequency_of_command{$command}++;
262 if (!$Commands_Description{$command}) {
263 $mywi_cache_for{$command} ||= mywi_process_query($command) || "";
264 my $mywi = join ("\n", grep(/\([18]|sh|script\)/, split(/\n/, $mywi_cache_for{$command})));
265 $mywi =~ s/\s+/ /;
266 if ($mywi !~ /^\s*$/) {
267 $Commands_Description{$command} = $mywi;
268 }
269 else {
270 next;
271 }
272 }
274 push @comments, $Commands_Description{$command};
275 }
276 return join("&#10;\n", @comments);
278 # Files
279 for my $arg (@args) {
280 $arg =~ s/'//g;
281 if (!$Args_Description{$arg}) {
282 my $mywi;
283 $mywi = mywi_client ($arg);
284 $mywi = join ("\n", grep(/\([5]\)/, split(/\n/, $mywi)));
285 $mywi =~ s/\s+/ /;
286 if ($mywi !~ /^\s*$/) {
287 $Args_Description{$arg} = $mywi;
288 }
289 else {
290 next;
291 }
292 }
294 push @comments, $Args_Description{$arg};
295 }
297 }
299 =cut
300 Процедура load_command_lines_from_xml выполняет загрузку разобранного lab-скрипта
301 из XML-документа в переменную @Command_Lines
303 # In: $datafile имя файла
304 # Out: @CommandLines загруженные командные строки
306 Предупреждение!
307 Процедура не в состоянии обрабатывать XML-документ любой структуры.
308 В действительности файл cache из которого загружаются данные
309 просто напоминает XML с виду.
310 =cut
311 sub load_command_lines_from_xml
312 {
313 my $datafile = $_[0];
315 open (CLASS, $datafile)
316 or die "Can't open file with xml lablog ",$datafile,"\n";
317 local $/;
318 binmode CLASS, ":utf8";
319 $data = <CLASS>;
320 close(CLASS);
322 for $command ($data =~ m@<command>(.*?)</command>@sg) {
323 my %cl;
324 while ($command =~ m@<([^>]*?)>(.*?)</\1>@sg) {
325 $cl{$1} = $2;
326 }
327 push @Command_Lines, \%cl;
328 }
329 }
331 sub load_sessions_from_xml
332 {
333 my $datafile = $_[0];
335 open (CLASS, $datafile)
336 or die "Can't open file with xml lablog ",$datafile,"\n";
337 local $/;
338 binmode CLASS, ":utf8";
339 my $data = <CLASS>;
340 close(CLASS);
342 my $i=0;
343 for my $session ($data =~ m@<session>(.*?)</session>@msg) {
344 my %session_hash;
345 while ($session =~ m@<([^>]*?)>(.*?)</\1>@sg) {
346 $session_hash{$1} = $2;
347 }
348 $Sessions{$session_hash{local_session_id}} = \%session_hash;
349 }
350 }
352 sub load_uploads($)
353 {
354 $dir=$_[0];
355 for $i (glob("$dir/*.png")) {
356 $i =~ s@.*/(([0-9-]+)_([0-9]+).*)@$1@;
357 if (defined($Uploads{$2}{$3})) {
358 $Uploads{$2}{$3} .= " ".$i;
359 }
360 else {
361 $Uploads{$2}{$3}=$i;
362 }
363 }
364 }
366 #for $key (sort keys %session) {
367 # for $t (sort { $a <=> $b } keys %{ $session{$key} }) {
368 # print $session{$key}{$t}."\n";
369 # }
370 #}
371 #}
373 # sort_command_lines
374 # In: @Command_Lines
375 # Out: @Command_Lies_Index
377 sub sort_command_lines
378 {
380 my @index;
381 for (my $i=0;$i<=$#Command_Lines;$i++) {
382 $index[$i]=$i;
383 }
385 @Command_Lines_Index = sort {
386 $Command_Lines[$index[$a]]->{"time"} <=> $Command_Lines[$index[$b]]->{"time"}
387 } @index;
389 }
391 ##################
392 # process_command_lines
393 #
394 # Обрабатываются командные строки @Command_Lines
395 # Для каждой строки определяется:
396 # class класс
397 # note комментарий
398 #
399 # In: @Command_Lines_Index
400 # In-Out: @Command_Lines
402 sub process_command_lines
403 {
406 my $current_command=0;
407 my $prev_i;
409 my $tab_seq =0 ; # номер команды в последовательности tab-completion
410 # отличен от нуля только для тех последовательностей,
411 # где постоянно нажимается клавиша tab
413 COMMAND_LINE_PROCESSING:
414 for my $i (@Command_Lines_Index) {
416 $current_command++;
417 next if $current_command < $Config{"start_from_command"};
418 last if $current_command > $Config{"start_from_command"} + $Config{"commands_to_show_at_a_go"};
420 my $cl = \$Command_Lines[$i];
422 # Запоминаем предыщуюу команду
423 # Она нам потребуется, в частности, для ввода tab_seq рпи обработке tab_completion
424 my $prev_cl;
425 $prev_cl = \$Command_Lines[$prev_i] if defined($prev_i);
426 $prev_i = $i;
428 next if !$cl;
430 for my $filter_key (keys %filter) {
431 next COMMAND_LINE_PROCESSING
432 if defined($$cl->{local_session_id})
433 && defined($Sessions{$$cl->{local_session_id}}->{$filter_key})
434 && $Sessions{$$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
435 }
437 $$cl->{id} = $$cl->{"time"};
439 $$cl->{err} ||=0;
442 # Класс команды
444 $$cl->{"class"} = $$cl->{"err"} eq 130 ? "interrupted"
445 : $$cl->{"err"} eq 127 ? "mistyped"
446 : $$cl->{"err"} ? "wrong"
447 : "normal";
449 if ($$cl->{"cline"} &&
450 $$cl->{"cline"} =~ /[^|`]\s*sudo/
451 || $$cl->{"uid"} eq 0) {
452 $$cl->{"class"}.="_root";
453 }
455 my $hint;
456 count_frequency_of_commands($$cl->{"cline"});
457 $hint = make_comment($$cl->{"cline"});
459 if ($hint) {
460 $$cl->{hint} = $hint;
461 }
462 $tigra_hints{$$cl->{"time"}} = $hint;
464 #$$cl->{hint}="";
466 # Выводим <head_lines> верхних строк
467 # и <tail_lines> нижних строк,
468 # если эти параметры существуют
469 my $output="";
471 if ($$cl->{"last_command"} eq "cat" && !$$cl->{"err"} && !($$cl->{"cline"} =~ /</)) {
472 my $filename = $$cl->{"cline"};
473 $filename =~ s/.*\s+(\S+)\s*$/$1/;
474 $Files{$filename}->{"content"} = $$cl->{"output"};
475 $Files{$filename}->{"source_command_id"} = $$cl->{"id"}
476 }
477 my @lines = split '\n', $$cl->{"output"};
478 if ((
479 $Config{"head_lines"}
480 || $Config{"tail_lines"}
481 )
482 && $#lines > $Config{"head_lines"} + $Config{"tail_lines"} ) {
483 #
484 for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
485 $output .= $lines[$i]."\n";
486 }
487 $output .= $Config{"skip_text"}."\n";
489 my $start_line=$#lines-$Config{"tail_lines"}+1;
490 for (my $i=$start_line; $i<= $#lines; $i++) {
491 $output .= $lines[$i]."\n";
492 }
493 }
494 else {
495 $output = $$cl->{"output"};
496 }
497 $$cl->{short_output} = $output;
499 # Обработка команд с одинаковым временем
500 # Скорее всего они набраны с помощью tab-completion
501 if (defined($prev_cl)) {
502 if ($$prev_cl->{time} == $$cl->{time} && $$prev_cl->{nonce} == $$cl->{nonce}) {
503 $tab_seq++;
504 }
505 else {
506 $tab_seq=0;
507 };
508 $$prev_cl->{tab_seq}=$tab_seq;
510 # Обработка команд с одинаковым номером в истории
511 # Скорее всего они набраны с помощью Ctrl-C
512 #if ($$prev_cl->{history} == $$cl->{history}) {
513 # $$prev_cl->{break}=1;
514 #}
515 }
518 #Обработка пометок
519 # Если несколько пометок (notes) идут подряд,
520 # они все объединяются
522 if ($$cl->{cline} =~ /l3shot/) {
523 if ($$cl->{output} =~ m@Screenshot is written to.*/(.*)\.xwd@) {
524 $$cl->{screenshot}="$1".$Config{l3shot_suffix};
525 }
526 }
527 if ($$cl->{cline} =~ /l3upload/) {
528 if ($$cl->{output} =~ m@Uploaded file name is (.*)@) {
529 $$cl->{screenshot}="$1";
530 }
531 }
533 if ($$cl->{cline}=~ m@cat[^#]*#([\^=v])\s*(.*)@) {
535 my $note_operator = $1;
536 my $note_title = $2;
538 if ($note_operator eq "=") {
539 $$cl->{"class"} = "note";
540 $$cl->{"note"} = $$cl->{"output"};
541 $$cl->{"note_title"} = $2;
542 }
543 else {
544 my $j = $i;
545 if ($note_operator eq "^") {
546 $j--;
547 $j-- while ($j >=0 && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
548 }
549 elsif ($note_operator eq "v") {
550 $j++;
551 $j++ while ($j <= @Command_Lines && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
552 }
553 $Command_Lines[$j]->{note_title}=$note_title;
554 $Command_Lines[$j]->{note}.=$$cl->{output};
555 $$cl=0;
556 }
557 }
558 elsif ($$cl->{cline}=~ /#([\^=v])(.*)/) {
560 my $note_operator = $1;
561 my $note_text = $2;
563 if ($note_operator eq "=") {
564 $$cl->{"class"} = "note";
565 $$cl->{"note"} = $note_text;
566 }
567 else {
568 my $j=$i;
569 if ($note_operator eq "^") {
570 $j--;
571 $j-- while ($j >=0 && (!$Command_Lines[$j] || $Command_Lines[$j]->{tty} ne $$cl->{tty}));
572 }
573 elsif ($note_operator eq "v") {
574 $j++;
575 $j++ while ($j <= @Command_Lines && $Command_Lines[$j]->{tty} ne $$cl->{tty} || !$Command_Lines[$j]);
576 }
577 $Command_Lines[$j]->{note}.="$note_text\n";
578 $$cl=0;
579 }
580 }
581 if ($$cl->{"class"} eq "note") {
582 my $note_html = $$cl->{note};
583 $note_html = join ("\n", map ("<p>$_</p>", split (/-\n/, $note_html)));
584 $note_html =~ s@(http:[a-zA-Z.0-9/?\_%-]*)@<a href='$1'>$1</a>@g;
585 $note_html =~ s@(www\.[a-zA-Z.0-9/?\_%-]*)@<a href='$1'>$1</a>@g;
586 $$cl->{"note_html"} = $note_html;
587 }
588 }
590 }
593 =cut
594 Процедура print_command_lines выводит HTML-представление
595 разобранного lab-скрипта.
597 Разобранный lab-скрипт должен находиться в массиве @Command_Lines
598 =cut
600 sub print_command_lines_html
601 {
603 my @toc; # Оглавление
604 my $note_number=0;
606 my $result = q();
607 my $this_day_resut = q();
609 my $cl;
610 my $last_tty="";
611 my $last_session="";
612 my $last_day=q();
613 my $last_wday=q();
614 my $first_command_of_the_day_unix_time=q();
615 my $human_readable_time=q();
616 my $in_range=0;
618 my $current_command=0;
620 my @known_commands;
624 $Stat{LastCommand} ||= 0;
625 $Stat{TotalCommands} ||= 0;
626 $Stat{ErrorCommands} ||= 0;
627 $Stat{MistypedCommands} ||= 0;
629 my %new_entries_of = (
630 "1 1" => "программы пользователя",
631 "2 8" => "программы администратора",
632 "3 sh" => "команды интерпретатора",
633 "4 script"=> "скрипты",
634 );
636 COMMAND_LINE:
637 for my $k (@Command_Lines_Index) {
639 my $cl=$Command_Lines[$Command_Lines_Index[$current_command++]];
640 next unless $cl;
641 my $next_cl=$Command_Lines[$Command_Lines_Index[$current_command]];
643 next if $current_command < $Config{"start_from_command"};
644 last if $current_command > $Config{"start_from_command"} + $Config{"commands_to_show_at_a_go"};
648 # Пропускаем строки, которые противоречат фильтру
649 # Если у нас недостаточно информации о том, подходит строка под фильтр или нет,
650 # мы её выводим
652 for my $filter_key (keys %filter) {
653 next COMMAND_LINE
654 if defined($cl->{local_session_id})
655 && defined($Sessions{$cl->{local_session_id}}->{$filter_key})
656 && $Sessions{$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
657 }
659 # Набираем статистику
660 # Хэш %Stat
662 $Stat{FirstCommand} = $cl->{time} unless $Stat{FirstCommand};
663 if ($cl->{time} - $Stat{LastCommand} < $Config{stat_inactivity_interval}) {
664 $Stat{TotalTime} += $cl->{time} - $Stat{LastCommand}
665 }
666 my $seconds_since_last_command = $cl->{time} - $Stat{LastCommand};
668 if ($Stat{LastCommand} > $cl->{time}) {
669 $result .= "Время идёт вспять<br/>";
670 };
671 $Stat{LastCommand} = $cl->{time};
672 $Stat{TotalCommands}++;
674 # Пропускаем строки, выходящие за границу "signature",
675 # при условии, что границы указаны
676 # Пропускаем неправильные/прерванные/другие команды
677 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
678 $in_range=1;
679 next;
680 }
681 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
682 $in_range=0;
683 next;
684 }
685 next if ($Config{"from"} && $Config{"to"} && !$in_range)
686 || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
687 || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
688 || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
693 #
694 ##
695 ## Начинается собственно вывод
696 ##
697 #
699 ### Сначала обрабатываем границы разделов
700 ### Если тип команды "note", это граница
702 if ($cl->{class} eq "note") {
703 $this_day_result .= "<tr><td colspan='6'>"
704 . "<h4 id='note$note_number'>".$cl->{note_title}."</h4>" if $cl->{note_title}
705 . "".$cl->{note_html}."<p/><p/></td></tr>";
707 if ($cl->{note_title}) {
708 push @{$toc[@toc]},"<a href='#note$note_number'>".$cl->{note_title}."</a>";
709 $note_number++;
710 }
711 next;
712 }
714 my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
717 # Добавляем спереди 0 для удобочитаемости
718 $min = "0".$min if $min =~ /^.$/;
719 $hour = "0".$hour if $hour =~ /^.$/;
720 $sec = "0".$sec if $sec =~ /^.$/;
722 $class=$cl->{"class"};
723 $Stat{ErrorCommands}++ if $class =~ /wrong/;
724 $Stat{MistypedCommands}++ if $class =~ /mistype/;
726 # DAY CHANGE
727 if ( $last_day ne $day) {
728 $prev_unix_time=$first_command_of_the_day_unix_time;
729 $first_command_of_the_day_unix_time = $cl->{time};
730 $human_readable_time = strftime "%D", localtime($prev_unix_time);
731 if ($last_day) {
733 # Вычисляем разность множеств.
734 # Что-то вроде этого, если бы так можно было писать:
735 # @new_commands = keys %frequency_of_command - @known_commands;
738 # Выводим предыдущий день
740 $result .= "<h3 id='day_on_sec_$prev_unix_time'>".$Day_Name[$last_wday]." ($human_readable_time)</h3>";
741 for my $entry_class (sort keys %new_entries_of) {
742 my $table_caption = "Таблица ".$table_number++.".".$Day_Name[$last_wday]
743 .". Новые ".$new_entries_of{$entry_class};
744 my $new_commands_section = make_new_entries_table(
745 $table_caption,
746 $entry_class=~/[0-9]+\s+(.*)/,
747 \@known_commands);
748 }
749 @known_commands = keys %frequency_of_command;
750 $result .= $this_day_result;
751 }
753 # Добавляем текущий день в оглавление
755 $human_readable_time = strftime "%D", localtime($first_command_of_the_day_unix_time);
756 push @toc, "<a href='#day_on_sec_$first_command_of_the_day_unix_time'>".$Day_Name[$wday]." ($human_readable_time)</a>\n";
759 $last_day=$day;
760 $last_wday=$wday;
761 $this_day_result = q();
762 }
763 else {
764 $this_day_result .= minutes_passed($seconds_since_last_command);
765 }
767 $this_day_result .= "<div class='command' id='command:".$cl->{"id"}."' >\n";
769 # CONSOLE CHANGE
770 if ($cl->{"tty"} && $last_tty ne $cl->{"tty"} && 0) {
771 my $tty = $cl->{"tty"};
772 $this_day_result .= "<div class='ttychange'>"
773 . $tty
774 ."</div>";
775 $last_tty=$cl->{"tty"};
776 }
778 # Session change
779 if ( $last_session ne $cl->{"local_session_id"}) {
780 my $tty;
781 if (defined $Sessions{$cl->{"local_session_id"}}->{"tty"}) {
782 $this_day_result .= "<div class='ttychange'><a href='?filter=local_session_id::".$cl->{"local_session_id"}."'>"
783 . $Sessions{$cl->{"local_session_id"}}->{"tty"}
784 ."</a></div>";
785 }
786 $last_session=$cl->{"local_session_id"};
787 }
789 # TIME
790 if ($Config{"show_time"} =~ /^y/i) {
791 $this_day_result .= "<div class='time'>$hour:$min:$sec</div>"
792 }
794 # COMMAND
795 my $cline;
796 $prompt_hint = join ("&#10;",
797 map("$_=$cl->{$_}",
798 grep (!/^(output|short_output|diff)$/,
799 sort(keys(%{$cl})))));
801 $cl->{"prompt"} =~ s/ $//;
802 $cline = "<span title='$prompt_hint' class='prompt'><a href='#".$cl->{time}."' id='".$cl->{time}."'>".$cl->{"prompt"}."</a></span>"
803 ."<span onmouseover=\"myHint.show('".$cl->{time}."')\" onmouseout=\"myHint.hide()\">".$cl->{"cline"}."</span>";
804 $cline =~ s/\n//;
806 if ($cl->{"hint"}) {
807 # $cline = "<span title='$cl->{hint}' class='with_hint'>$cline</span>" ;
808 $cline = "<span class='with_hint'>$cline</span>" ;
809 }
810 else {
811 $cline = "<span class='without_hint'>$cline</span>";
812 }
814 $this_day_result .= "<DIV class='fixed_div'><table cellpadding='0' cellspacing='0'><tr><td>\n<div class='cblock_$cl->{class}'>\n";
815 $this_day_result .= "<div class='cline'>" . $cline ; #cline
816 $this_day_result .= "<span title='Код завершения ".$cl->{"err"}."'>\n"
817 . "<img src='".$Config{frontend_ico_path}."/error.png'/>\n"
818 . "</span>\n" if ($cl->{"err"} and not $cl->{tab_seq} and not $cl->{break});
819 $this_day_result .= "<span title='Tab completion ".$cl->{tab_seq}."'>\n"
820 . "<img src='".$Config{frontend_ico_path}."/tab.png'/>\n"
821 . "</span>\n" if $cl->{tab_seq};
822 $this_day_result .= "<span title='Ctrl-C pressed'>\n"
823 . "<img src='".$Config{frontend_ico_path}."/break.png'/>\n"
824 . "</span>\n" if ($cl->{break} and not $cl->{tab_seq});
825 $this_day_result .= "</div>\n"; #cline
827 # OUTPUT
828 my $last_command = $cl->{"last_command"};
829 if (!(
830 $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
831 $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
832 $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
833 )) {
834 $this_day_result .= "<pre class='output'>\n" . $cl->{short_output} . "</pre>\n";
835 }
837 # DIFF
838 $this_day_result .= "<pre class='diff'>".$cl->{"diff"}."</pre>"
839 if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"});
840 # SHOT
842 #$this_day_result .= join(".", keys(%Uploads));
843 #$this_day_result .= "PRIVET";
844 for $t (sort { $a <=> $b } keys %{ $Uploads{$cl->{"local_session_id"}} }) {
845 if (($t >= $cl->{"time"} and $t < $next_cl->{"time"}) or ($t >= $cl->{"time"} and not defined($next_cl))) {
846 my @shots=split(/\s+/, $Uploads{$cl->{"local_session_id"}}{$t});
847 for my $shot (@shots) {
848 $this_day_result .= "<IMG src='"
849 .$Config{l3shot_path}
850 .$shot
851 ."' alt ='screenshot id ".$shot
852 ."'/><br/>"
853 }
854 }
855 }
857 # Временно заблокировано
858 # $this_day_result .= "<img src='"
859 # .$Config{l3shot_path}
860 # .$cl->{"screenshot"}
861 # ."' alt ='screenshot id ".$cl->{"screenshot"}
862 # ."'/>"
863 # if ( $Config{"show_screenshots"} =~ /^y/i && $cl->{"screenshot"});
865 #NOTES
866 if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
867 my $note=$cl->{"note"};
868 $note =~ s/\n/<br\/>\n/msg;
869 if (not $note =~ s@(http:[a-zA-Z.0-9/_?%-]*)@<a href='$1'>$1</a>@g) {
870 $note =~ s@(www\.[a-zA-Z.0-9/_?%-]*)@<a href='$1'>$1</a>@g;
871 };
872 $this_day_result .= "<div class='note'>";
873 $this_day_result .= "<div class='note_title'>".$cl->{note_title}."</div>" if $cl->{note_title};
874 $this_day_result .= "<div class='note_text'>".$note."</div>";
875 $this_day_result .= "</div>\n";
876 }
878 # Вывод очередной команды окончен
879 $this_day_result .= "</div>\n"; # cblock
880 $this_day_result .= "</td></tr></table></DIV>\n"
881 . "</div>\n"; # command
882 }
883 last: {
884 $prev_unix_time=$first_command_of_the_day_unix_time;
885 $first_command_of_the_day_unix_time = $cl->{time};
886 $human_readable_time = strftime "%D", localtime($prev_unix_time);
888 $result .= "<h3 id='day_on_sec_$prev_unix_time'>".$Day_Name[$last_wday]." ($human_readable_time)</h3>";
890 for my $entry_class (keys %new_entries_of) {
891 my $table_caption = "Таблица ".$table_number++.".".$Day_Name[$last_wday]
892 . ". Новые ".$new_entries_of{$entry_class};
893 my $new_commands_section = make_new_entries_table(
894 $table_caption,
895 $entry_class=~/[0-9]+\s+(.*)/,
896 \@known_commands);
897 }
898 @known_commands = keys %frequency_of_command;
899 $result .= $this_day_result;
900 }
902 return ($result, collapse_list (\@toc));
904 }
906 #############
907 # make_new_entries_table
908 #
909 # Напечатать таблицу неизвестных команд
910 #
911 # In: $_[0] table_caption
912 # $_[1] entries_class
913 # @_[2..] known_commands
914 # Out:
916 sub make_new_entries_table
917 {
918 my $table_caption;
919 my $entries_class = shift;
920 my @known_commands = @{$_[0]};
921 my $result = "";
923 my %count;
924 my @new_commands = ();
925 for my $c (keys %frequency_of_command, @known_commands) {
926 $count{$c}++
927 }
928 for my $c (keys %frequency_of_command) {
929 push @new_commands, $c if $count{$c} != 2;
930 }
932 my $new_commands_section;
933 if (@new_commands){
934 my $hint;
935 for my $c (reverse sort { $frequency_of_command{$a} <=> $frequency_of_command{$b} } @new_commands) {
936 $hint = make_comment($c);
937 next unless $hint;
938 my ($command, $hint) = $hint =~ m/(.*?) \s*- \s*(.*)/;
939 next unless $command =~ s/\($entries_class\)//i;
940 $new_commands_section .= "<tr><td valign='top'>$command</td><td>$hint</td></tr>";
941 }
942 }
943 if ($new_commands_section) {
944 $result .= "<table class='new_commands_table' width='700' cellspacing='0' cellpadding='0'>"
945 . "<tr class='new_commands_caption'>"
946 . "<td colspan='2' align='right'>$table_caption</td>"
947 . "</tr>"
948 . "<tr class='new_commands_header'>"
949 . "<td width=100>Команда</td><td width=600>Описание</td>"
950 . "</tr>"
951 . $new_commands_section
952 . "</table>"
953 }
954 return $result;
955 }
957 #############
958 # minutes_passed
959 #
960 #
961 #
962 # In: $_[0] seconds_since_last_command
963 # Out: "minutes passed" text
965 sub minutes_passed
966 {
967 my $seconds_since_last_command = shift;
968 my $result = "";
969 if ($seconds_since_last_command > 7200) {
970 my $hours_passed = int($seconds_since_last_command/3600);
971 my $passed_word = $hours_passed % 10 == 1 ? "прошла"
972 : "прошло";
973 my $hours_word = $hours_passed % 10 == 1 ? "часа":
974 "часов";
975 $result .= "<div class='much_time_passed'>"
976 . $passed_word." &gt;".$hours_passed." ".$hours_word
977 . "</div>\n";
978 }
979 elsif ($seconds_since_last_command > 600) {
980 my $minutes_passed = int($seconds_since_last_command/60);
983 my $passed_word = $minutes_passed % 100 > 10
984 && $minutes_passed % 100 < 20 ? "прошло"
985 : $minutes_passed % 10 == 1 ? "прошла"
986 : "прошло";
988 my $minutes_word = $minutes_passed % 100 > 10
989 && $minutes_passed % 100 < 20 ? "минут" :
990 $minutes_passed % 10 == 1 ? "минута":
991 $minutes_passed % 10 == 0 ? "минут" :
992 $minutes_passed % 10 > 4 ? "минут" :
993 "минуты";
995 if ($seconds_since_last_command < 1800) {
996 $result .= "<div class='time_passed'>"
997 . $passed_word." ".$minutes_passed." ".$minutes_word
998 . "</div>\n";
999 }
1000 else {
1001 $result .= "<div class='much_time_passed'>"
1002 . $passed_word." ".$minutes_passed." ".$minutes_word
1003 . "</div>\n";
1006 return $result;
1009 #############
1010 # print_all_txt
1012 # Вывести журнал в текстовом формате
1014 # In: $_[0] output_filename
1015 # Out:
1017 sub print_command_lines_txt
1020 my $output_filename=$_[0];
1021 my $note_number=0;
1023 my $result = q();
1024 my $this_day_resut = q();
1026 my $cl;
1027 my $last_tty="";
1028 my $last_session="";
1029 my $last_day=q();
1030 my $last_wday=q();
1031 my $in_range=0;
1033 my $current_command=0;
1035 my $cursor_position = 0;
1038 if ($Config{filter}) {
1039 # Инициализация фильтра
1040 for (split /&/,$Config{filter}) {
1041 my ($var, $val) = split /::/;
1042 $filter{$var} = $val || "";
1047 COMMAND_LINE:
1048 for my $k (@Command_Lines_Index) {
1050 my $cl=$Command_Lines[$Command_Lines_Index[$current_command++]];
1051 next unless $cl;
1054 # Пропускаем строки, которые противоречат фильтру
1055 # Если у нас недостаточно информации о том, подходит строка под фильтр или нет,
1056 # мы её выводим
1058 for my $filter_key (keys %filter) {
1059 next COMMAND_LINE
1060 if defined($cl->{local_session_id})
1061 && defined($Sessions{$cl->{local_session_id}}->{$filter_key})
1062 && $Sessions{$cl->{local_session_id}}->{$filter_key} ne $filter{$filter_key};
1065 # Пропускаем строки, выходящие за границу "signature",
1066 # при условии, что границы указаны
1067 # Пропускаем неправильные/прерванные/другие команды
1068 if ($Config{"from"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"from"}/) {
1069 $in_range=1;
1070 next;
1072 if ($Config{"to"} && $cl->{"cline"} =~ /$Config{"signature"}\s*$Config{"to"}/) {
1073 $in_range=0;
1074 next;
1076 next if ($Config{"from"} && $Config{"to"} && !$in_range)
1077 || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
1078 || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
1079 || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
1083 ##
1084 ## Начинается собственно вывод
1085 ##
1088 ### Сначала обрабатываем границы разделов
1089 ### Если тип команды "note", это граница
1091 if ($cl->{class} eq "note") {
1092 $this_day_result .= " === ".$cl->{note_title}." === \n" if $cl->{note_title};
1093 $this_day_result .= $cl->{note}."\n";
1094 next;
1097 my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
1099 # Добавляем спереди 0 для удобочитаемости
1100 $min = "0".$min if $min =~ /^.$/;
1101 $hour = "0".$hour if $hour =~ /^.$/;
1102 $sec = "0".$sec if $sec =~ /^.$/;
1104 $class=$cl->{"class"};
1106 # DAY CHANGE
1107 if ( $last_day ne $day) {
1108 if ($last_day) {
1109 $result .= "== ".$Day_Name[$last_wday]." == \n";
1110 $result .= $this_day_result;
1112 $last_day = $day;
1113 $last_wday = $wday;
1114 $this_day_result = q();
1117 # CONSOLE CHANGE
1118 if ($cl->{"tty"} && $last_tty ne $cl->{"tty"} && 0) {
1119 my $tty = $cl->{"tty"};
1120 $this_day_result .= " #l3: ------- другая консоль ----\n";
1121 $last_tty=$cl->{"tty"};
1124 # Session change
1125 if ( $last_session ne $cl->{"local_session_id"}) {
1126 $this_day_result .= "# ------------------------------------------------------------"
1127 . " l3: local_session_id=".$cl->{"local_session_id"}
1128 . " ---------------------------------- \n";
1129 $last_session=$cl->{"local_session_id"};
1132 # TIME
1133 my @nl_counter = split (/\n/, $result);
1134 $cursor_position=length($result) - @nl_counter;
1136 if ($Config{"show_time"} =~ /^y/i) {
1137 $this_day_result .= "$hour:$min:$sec"
1140 # COMMAND
1141 $this_day_result .= " ".$cl->{"prompt"}.$cl->{"cline"}."\n";
1142 if ($cl->{"err"}) {
1143 $this_day_result .= " #l3: err=".$cl->{'err'}."\n";
1146 # OUTPUT
1147 my $last_command = $cl->{"last_command"};
1148 if (!(
1149 $Config{"suppress_editors"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"editors"}}) ||
1150 $Config{"suppress_pagers"} =~ /^y/i && grep ($_ eq $last_command, @{$Config{"pagers"}}) ||
1151 $Config{"suppress_terminal"}=~ /^y/i && grep ($_ eq $last_command, @{$Config{"terminal"}})
1152 )) {
1153 my $output = $cl->{short_output};
1154 if ($output) {
1155 $output =~ s/^/ |/mg;
1157 $this_day_result .= $output;
1160 # DIFF
1161 if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
1162 my $diff = $cl->{"diff"};
1163 $diff =~ s/^/ |/mg;
1164 $this_day_result .= $diff;
1165 };
1166 # SHOT
1167 if ($Config{"show_screenshots"} =~ /^y/i && $cl->{"screenshot"}) {
1168 $this_day_result .= " #l3: screenshot=".$cl->{'screenshot'}."\n";
1171 #NOTES
1172 if ( $Config{"show_notes"} =~ /^y/i && $cl->{"note"}) {
1173 my $note=$cl->{"note"};
1174 $note =~ s/\n/\n#^/msg;
1175 $this_day_result .= "#^ == ".$cl->{note_title}." ==\n" if $cl->{note_title};
1176 $this_day_result .= "#^ ".$note."\n";
1180 last: {
1181 $result .= "== ".$Day_Name[$last_wday]." == \n";
1182 $result .= $this_day_result;
1185 return $result;
1191 #############
1192 # print_edit_all_html
1194 # Вывести страницу с текстовым представлением журнала для редактирования
1196 # In: $_[0] output_filename
1197 # Out:
1199 sub print_edit_all_html
1201 my $output_filename= shift;
1202 my $result;
1203 my $cursor_position = 0;
1205 $result = print_command_lines_txt;
1206 my $title = ">Журнал лабораторных работ. Правка";
1208 $result =
1209 "<html>"
1210 ."<head>"
1211 ."<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />"
1212 ."<link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>"
1213 ."<title>$title</title>"
1214 ."</head>"
1215 ."<script>"
1216 .$SetCursorPosition_JS
1217 ."</script>"
1218 ."<body onLoad='setCursorPosition(document.all.mytextarea, $cursor_position, $cursor_position+10)'>"
1219 ."<h1>Журнал лабораторных работ. Правка</h1>"
1220 ."<form>"
1221 ."<textarea rows='30' cols='100' wrap='off' id='mytextarea'>$result</textarea>"
1222 ."<br/><input type='submit' value='Сохранить' label='label'/>"
1223 ."</form>"
1224 ."<p>Внимательно правим, потом сохраняем</p>"
1225 ."<p>Строки, начинающиеся символами #l3: можно трогать, только если точно знаешь, что делаешь</p>"
1226 ."</body>"
1227 ."</html>";
1229 if ($output_filename eq "-") {
1230 print $result;
1232 else {
1233 open(OUT, ">", $output_filename)
1234 or die "Can't open $output_filename for writing\n";
1235 binmode ":utf8";
1236 print OUT "$result";
1237 close(OUT);
1241 #############
1242 # print_all_txt
1244 # Вывести страницу с текстовым представлением журнала для редактирования
1246 # In: $_[0] output_filename
1247 # Out:
1249 sub print_all_txt
1251 my $result;
1253 $result = print_command_lines_txt;
1255 $result =~ s/&gt;/>/g;
1256 $result =~ s/&lt;/</g;
1257 $result =~ s/&amp;/&/g;
1259 if ($output_filename eq "-") {
1260 print $result;
1262 else {
1263 open(OUT, ">:utf8", $output_filename)
1264 or die "Can't open $output_filename for writing\n";
1265 print OUT "$result";
1266 close(OUT);
1271 #############
1272 # print_all_html
1276 # In: $_[0] output_filename
1277 # Out:
1280 sub print_all_html
1282 my $output_filename=$_[0];
1284 my $result;
1285 my ($command_lines,$toc) = print_command_lines_html;
1286 my $files_section = print_files_html;
1288 $result = $debug_output;
1289 $result .= print_header_html($toc);
1292 # $result.= join " <br/>", keys %Sessions;
1293 # for my $sess (keys %Sessions) {
1294 # $result .= join " ", keys (%{$Sessions{$sess}});
1295 # $result .= "<br/>";
1296 # }
1298 $result.= "<h2 id='log'>Журнал</h2>" . $command_lines;
1299 $result.= "<h2 id='files'>Файлы</h2>" . $files_section if $files_section;
1300 $result.= "<h2 id='stat'>Статистика</h2>" . print_stat_html;
1301 $result.= "<h2 id='help'>Справка</h2>" . $Html_Help . "<br/>";
1302 $result.= "<h2 id='about'>О программе</h2>". $Html_About. "<br/>";
1303 $result.= print_footer_html;
1305 if ($output_filename eq "-") {
1306 binmode STDOUT, ":utf8";
1307 print $result;
1309 else {
1310 open(OUT, ">:utf8", $output_filename)
1311 or die "Can't open $output_filename for writing\n";
1312 print OUT $result;
1313 close(OUT);
1317 #############
1318 # print_header_html
1322 # In: $_[0] Содержание
1323 # Out: Распечатанный заголовок
1325 sub print_header_html
1327 my $toc = $_[0];
1328 my $course_name = $Config{"course-name"};
1329 my $course_code = $Config{"course-code"};
1330 my $course_date = $Config{"course-date"};
1331 my $course_center = $Config{"course-center"};
1332 my $course_trainer = $Config{"course-trainer"};
1333 my $course_student = $Config{"course-student"};
1335 my $title = "Журнал лабораторных работ";
1336 $title .= " -- ".$course_student if $course_student;
1337 if ($course_date) {
1338 $title .= " -- ".$course_date;
1339 $title .= $course_code ? "/".$course_code
1340 : "";
1342 else {
1343 $title .= " -- ".$course_code if $course_code;
1346 # Управляющая форма
1347 my $control_form .= "<div class='visibility_form' title='Выберите какие элементы должны быть показаны в журнале'>"
1348 . "<span class='header'>Видимые элементы</span>"
1349 . "<span class='window_controls'><a href='' onclick='' title='свернуть форму управления'>_</a> <a href='' onclick='' title='закрыть форму управления'>x</a></span>"
1350 . "<div><form>\n";
1351 for my $element (sort keys %Elements_Visibility)
1353 my ($skip, @e) = split /\s+/, $element;
1354 my $showhide = join "", map { "ShowHide('$_');" } @e ;
1355 $control_form .= "<div><input type='checkbox' name='$e[0]' onclick=\"$showhide\" checked>".
1356 $Elements_Visibility{$element}.
1357 "</input></div>";
1359 $control_form .= "</form>\n"
1360 . "</div>\n";
1363 # Управляющая форма отключена
1364 # Она слишком сильно мешает, нужно что-то переделать
1365 $control_form = "";
1367 my $tigra_hints_array=tigra_hints_generate;
1369 my $result;
1370 $result = <<HEADER;
1371 <html>
1372 <head>
1373 <meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
1374 <link rel='stylesheet' href='$Config{frontend_css}' type='text/css'/>
1375 <title>$title</title>
1376 </head>
1377 <body>
1378 <!--<script>
1379 $Html_JavaScript
1380 </script>-->
1382 <!-- vvv Tigra Hints vvv -->
1383 <script language="JavaScript" src="/tigra/hints.js"></script>
1384 <!--<script language="JavaScript" src="/tigra/hints_cfg.js"></script>-->
1385 <script>$tigra_hints_array</script>
1386 <style>
1387 /* a class for all Tigra Hints boxes, TD object */
1388 .hintsClass
1389 {text-align: left; font-size:80%; font-family: Verdana, Arial, Helvetica; background-color:#ffffee; padding: 0px 0px 0px 0px;}
1390 /* this class is used by Tigra Hints wrappers */
1391 .row
1392 {background: white;}
1395 .bl2 {border: 1px solid #e68200; background:url(/tigra/block/bl2.gif) 0 100% no-repeat; text-align:left}
1396 .bl {background:url(/tigra/block/bl2.gif) 0 100% no-repeat; text-align:left}
1397 .br {background:url(/tigra/block/br2.gif) 100% 100% no-repeat}
1398 .tl {background:url(/tigra/block/tl2.gif) 0 0 no-repeat}
1399 .tr {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat; padding:10px}
1400 .tr2 {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat}
1401 .t {background:url(/tigra/block/dot2.gif) 0 0 repeat-x}
1402 .b {background:url(/tigra/block/dot2.gif) 0 100% repeat-x}
1403 .l {background:url(/tigra/block/dot2.gif) 0 0 repeat-y}
1404 .r {background:url(/tigra/block/dot2.gif) 100% 0 repeat-y}
1407 </style>
1408 <!-- ^^^ Tigra Hints ^^^ -->
1410 <!--
1411 .bl2 {border: 1px solid #e68200; background:url(/tigra/block/bl2.gif) 0 100% no-repeat; width:20em; text-align:center}
1412 .bl {background:url(/tigra/block/bl2.gif) 0 100% no-repeat; width:20em; text-align:center}
1413 .br {background:url(/tigra/block/br2.gif) 100% 100% no-repeat}
1414 .tl {background:url(/tigra/block/tl2.gif) 0 0 no-repeat}
1415 .tr {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat; padding:10px}
1416 .tr2 {background:url(/tigra/block/tr2.gif) 100% 0 no-repeat}
1417 .t {background:url(/tigra/block/dot2.gif) 0 0 repeat-x; width:20em}
1418 .b {background:url(/tigra/block/dot2.gif) 0 100% repeat-x}
1419 .l {background:url(/tigra/block/dot2.gif) 0 0 repeat-y}
1420 .r {background:url(/tigra/block/dot2.gif) 100% 0 repeat-y}
1421 -->
1424 <div class='edit_link'>
1425 [ <a href='?filter=action::edit;;$filter_url'>править</a> ]
1426 </div>
1427 <h1 onmouseover="myHint.show('1')" onmouseout="myHint.hide()" class='lined_header'>Журнал лабораторных работ</h1>
1428 HEADER
1429 if ( $course_student
1430 || $course_trainer
1431 || $course_name
1432 || $course_code
1433 || $course_date
1434 || $course_center) {
1435 $result .= "<p>";
1436 $result .= "Выполнил $course_student<br/>" if $course_student;
1437 $result .= "Проверил $course_trainer <br/>" if $course_trainer;
1438 $result .= "Курс " if $course_name
1439 || $course_code
1440 || $course_date;
1441 $result .= "$course_name " if $course_name;
1442 $result .= "($course_code)" if $course_code;
1443 $result .= ", $course_date<br/>" if $course_date;
1444 $result .= "Учебный центр $course_center <br/>" if $course_center;
1445 $result .= "Фильтр ".join(" ", map("$filter{$_}=$_", keys %filter))."<br/>" if %filter;
1446 $result .= "</p>";
1449 $result .= <<HEADER;
1450 <table width='100%'>
1451 <tr>
1452 <td width='*'>
1454 <table border=0 id='toc' class='toc'>
1455 <tr>
1456 <td>
1457 <div class='toc_title'>Содержание</div>
1458 <ul>
1459 <li><a href='#log'>Журнал</a></li>
1460 <ul>$toc</ul>
1461 <li><a href='#files'>Файлы</a></li>
1462 <li><a href='#stat'>Статистика</a></li>
1463 <li><a href='#help'>Справка</a></li>
1464 <li><a href='#about'>О программе</a></li>
1465 </ul>
1466 </td>
1467 </tr>
1468 </table>
1470 </td>
1471 <td valign='top' width=200>$control_form</td>
1472 </tr>
1473 </table>
1474 HEADER
1476 return $result;
1480 #############
1481 # print_footer_html
1488 sub print_footer_html
1490 return "</body>\n</html>\n";
1496 #############
1497 # print_stat_html
1501 # In:
1502 # Out:
1504 sub print_stat_html
1506 %StatNames = (
1507 FirstCommand => "Время первой команды журнала",
1508 LastCommand => "Время последней команды журнала",
1509 TotalCommands => "Количество командных строк в журнале",
1510 ErrorsPercentage => "Процент команд с ненулевым кодом завершения, %",
1511 MistypesPercentage => "Процент синтаксически неверно набранных команд, %",
1512 TotalTime => "Суммарное время работы с терминалом <sup><font size='-2'>*</font></sup>, час",
1513 CommandsPerTime => "Количество командных строк в единицу времени, команда/мин",
1514 CommandsFrequency => "Частота использования команд",
1515 RareCommands => "Частота использования этих команд < 0.5%",
1516 );
1517 @StatOrder = (
1518 FirstCommand,
1519 LastCommand,
1520 TotalCommands,
1521 ErrorsPercentage,
1522 MistypesPercentage,
1523 TotalTime,
1524 CommandsPerTime,
1525 CommandsFrequency,
1526 RareCommands,
1527 );
1529 # Подготовка статистики к выводу
1530 # Некоторые значения пересчитываются!
1531 # Дальше их лучше уже не использовать!!!
1533 my %CommandsFrequency = %frequency_of_command;
1535 $Stat{TotalTime} ||= 0;
1536 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{FirstCommand} || 0);
1537 $Stat{FirstCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec, $year+1900, $mon+1, $mday;
1538 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Stat{LastCommand} || 0);
1539 $Stat{LastCommand} = sprintf "%02i:%02i:%02i %04i-%2i-%2i", $hour, $min, $sec, $year+1900, $mon+1, $mday;
1540 if ($Stat{TotalCommands}) {
1541 $Stat{ErrorsPercentage} = sprintf "%5.2f", $Stat{ErrorCommands}*100/$Stat{TotalCommands};
1542 $Stat{MistypesPercentage} = sprintf "%5.2f", $Stat{MistypedCommands}*100/$Stat{TotalCommands};
1544 $Stat{CommandsPerTime} = sprintf "%5.2f", $Stat{TotalCommands}*60/$Stat{TotalTime}
1545 if $Stat{TotalTime};
1546 $Stat{TotalTime} = sprintf "%5.2f", $Stat{TotalTime}/60/60;
1548 my $total_commands=0;
1549 for $command (keys %CommandsFrequency){
1550 $total_commands += $CommandsFrequency{$command};
1552 if ($total_commands) {
1553 for $command (reverse sort {$CommandsFrequency{$a} <=> $CommandsFrequency{$b}} keys %CommandsFrequency){
1554 my $command_html;
1555 my $percentage = sprintf "%5.2f",$CommandsFrequency{$command}*100/$total_commands;
1556 if ($percentage < 0.5) {
1557 my $hint = make_comment($command);
1558 $command_html = "$command";
1559 $command_html = "<span title='$hint' class='with_hint'>$command_html</span>" if $hint;
1560 $command_html = "<span class='without_hint'>$command_html</span>" if not $hint;
1561 my $command_html = "<tt>$command_html</tt>";
1562 $Stat{RareCommands} .= $command_html."<sub><font size='-2'>".$CommandsFrequency{$command}."</font></sub> , ";
1564 else {
1565 my $hint = make_comment($command);
1566 $command_html = "$command";
1567 $command_html = "<span title='$hint' class='with_hint'>$command_html</span>" if $hint;
1568 $command_html = "<span class='without_hint'>$command_html</span>" if not $hint;
1569 my $command_html = "<tt>$command_html</tt>";
1570 $percentage = sprintf "%5.2f",$percentage;
1571 $Stat{CommandsFrequency} .= "<tr><td>".$command_html."</td><td>".$CommandsFrequency{$command}."</td>".
1572 "<td>|".("="x int($CommandsFrequency{$command}*100/$total_commands))."| $percentage%</td></tr>";
1575 $Stat{CommandsFrequency} = "<table>".$Stat{CommandsFrequency}."</table>";
1576 $Stat{RareCommands} =~ s/, $// if $Stat{RareCommands};
1579 my $result = q();
1580 for my $stat (@StatOrder) {
1581 next unless $Stat{"$stat"};
1582 $result .= "<tr valign='top'><td width='300'>".$StatNames{"$stat"}."</td><td>".$Stat{"$stat"}."</td></tr>"
1584 $result = "<table>$result</table>"
1585 . "<font size='-2'>____<br/>*) Интервалы неактивности длительностью "
1586 . ($Config{stat_inactivity_interval}/60)
1587 . " минут и более не учитываются</font></br>";
1589 return $result;
1593 sub collapse_list($)
1595 my $res = "";
1596 for my $elem (@{$_[0]}) {
1597 if (ref $elem eq "ARRAY") {
1598 $res .= "<ul>".collapse_list($elem)."</ul>";
1600 else
1602 $res .= "<li>".$elem."</li>";
1605 return $res;
1609 sub print_files_html
1611 my $result = qq();
1612 my @toc;
1613 for my $file (sort keys %Files) {
1614 my $div_id = "file:$file";
1615 $div_id =~ s@/@_@g;
1616 push @toc, "<a href='#$div_id'>$file</a>";
1617 $result .= "<div class='filename' id='$div_id'>".$file."</div>\n"
1618 . "<div class='file_navigation'><a href='#command:".$Files{$file}->{source_command_id}."'>"."&gt;"."</a></div>"
1619 . "<div class='filedata'><pre>".$Files{$file}->{content}."</pre></div>";
1621 if ($result) {
1622 return "<div class='files_toc'>".collapse_list(\@toc)."</div>".$result;
1624 else {
1625 return "";
1630 sub init_variables
1632 $Html_Help = <<HELP;
1633 Для того чтобы использовать LiLaLo, не нужно знать ничего особенного:
1634 всё происходит само собой.
1635 Однако, чтобы ведение и последующее использование журналов
1636 было как можно более эффективным, желательно иметь в виду следующее:
1637 <ol>
1638 <li><p>
1639 В журнал автоматически попадают все команды, данные в любом терминале системы.
1640 </p></li>
1641 <li><p>
1642 Для того чтобы убедиться, что журнал на текущем терминале ведётся,
1643 и команды записываются, дайте команду w.
1644 В поле WHAT, соответствующем текущему терминалу,
1645 должна быть указана программа script.
1646 </p></li>
1647 <li><p>
1648 Команды, при наборе которых были допущены синтаксические ошибки,
1649 выводятся перечёркнутым текстом:
1650 <table>
1651 <tr class='command'>
1652 <td class='script'>
1653 <pre class='_mistyped_cline'>
1654 \$ l s-l</pre>
1655 <pre class='_mistyped_output'>bash: l: command not found
1656 </pre>
1657 </td>
1658 </tr>
1659 </table>
1660 <br/>
1661 </p></li>
1662 <li><p>
1663 Если код завершения команды равен нулю,
1664 команда была выполнена без ошибок.
1665 Команды, код завершения которых отличен от нуля, выделяются цветом.
1666 <table>
1667 <tr class='command'>
1668 <td class='script'>
1669 <pre class='_wrong_cline'>
1670 \$ test 5 -lt 4</pre>
1671 </pre>
1672 </td>
1673 </tr>
1674 </table>
1675 Обратите внимание на то, что код завершения команды может быть отличен от нуля
1676 не только в тех случаях, когда команда была выполнена с ошибкой.
1677 Многие команды используют код завершения, например, для того чтобы показать результаты проверки
1678 <br/>
1679 </p></li>
1680 <li><p>
1681 Команды, ход выполнения которых был прерван пользователем, выделяются цветом.
1682 <table>
1683 <tr class='command'>
1684 <td class='script'>
1685 <pre class='_interrupted_cline'>
1686 \$ find / -name abc</pre>
1687 <pre class='interrupted_output'>find: /home/devi-orig/.gnome2: Keine Berechtigung
1688 find: /home/devi-orig/.gnome2_private: Keine Berechtigung
1689 find: /home/devi-orig/.nautilus/metafiles: Keine Berechtigung
1690 find: /home/devi-orig/.metacity: Keine Berechtigung
1691 find: /home/devi-orig/.inkscape: Keine Berechtigung
1692 ^C
1693 </pre>
1694 </td>
1695 </tr>
1696 </table>
1697 <br/>
1698 </p></li>
1699 <li><p>
1700 Команды, выполненные с привилегиями суперпользователя,
1701 выделяются слева красной чертой.
1702 <table>
1703 <tr class='command'>
1704 <td class='script'>
1705 <pre class='_root_cline'>
1706 # id</pre>
1707 <pre class='_root_output'>
1708 uid=0(root) gid=0(root) Gruppen=0(root)
1709 </pre>
1710 </td>
1711 </tr>
1712 </table>
1713 <br/>
1714 </p></li>
1715 <li><p>
1716 Изменения, внесённые в текстовый файл с помощью редактора,
1717 запоминаются и показываются в журнале в формате ed.
1718 Строки, начинающиеся символом "&lt;", удалены, а строки,
1719 начинающиеся символом "&gt;" -- добавлены.
1720 <table>
1721 <tr class='command'>
1722 <td class='script'>
1723 <pre class='cline'>
1724 \$ vi ~/.bashrc</pre>
1725 <table><tr><td width='5'/><td class='diff'><pre>2a3,5
1726 &gt; if [ -f /usr/local/etc/bash_completion ]; then
1727 &gt; . /usr/local/etc/bash_completion
1728 &gt; fi
1729 </pre></td></tr></table></td>
1730 </tr>
1731 </table>
1732 <br/>
1733 </p></li>
1734 <li><p>
1735 Для того чтобы изменить файл в соответствии с показанными в диффшоте
1736 изменениями, можно воспользоваться командой patch.
1737 Нужно скопировать изменения, запустить программу patch, указав в
1738 качестве её аргумента файл, к которому применяются изменения,
1739 и всавить скопированный текст:
1740 <table>
1741 <tr class='command'>
1742 <td class='script'>
1743 <pre class='cline'>
1744 \$ patch ~/.bashrc</pre>
1745 </td>
1746 </tr>
1747 </table>
1748 В данном случае изменения применяются к файлу ~/.bashrc
1749 </p></li>
1750 <li><p>
1751 Для того чтобы получить краткую справочную информацию о команде,
1752 нужно подвести к ней мышь. Во всплывающей подсказке появится краткое
1753 описание команды.
1754 </p>
1755 <p>
1756 Если справочная информация о команде есть,
1757 команда выделяется голубым фоном, например: <span class="with_hint" title="главный текстовый редактор Unix">vi</span>.
1758 Если справочная информация отсутствует,
1759 команда выделяется розовым фоном, например: <span class="without_hint">notepad.exe</span>.
1760 Справочная информация может отсутствовать в том случае,
1761 если (1) команда введена неверно; (2) если распознавание команды LiLaLo выполнено неверно;
1762 (3) если информация о команде неизвестна LiLaLo.
1763 Последнее возможно для редких команд.
1764 </p></li>
1765 <li><p>
1766 Большие, в особенности многострочные, всплывающие подсказки лучше
1767 всего показываются браузерами KDE Konqueror, Apple Safari и Microsoft Internet Explorer.
1768 В браузерах Mozilla и Firefox они отображаются не полностью,
1769 а вместо перевода строки выводится специальный символ.
1770 </p></li>
1771 <li><p>
1772 Время ввода команды, показанное в журнале, соответствует времени
1773 <i>начала ввода командной строки</i>, которое равно тому моменту,
1774 когда на терминале появилось приглашение интерпретатора
1775 </p></li>
1776 <li><p>
1777 Имя терминала, на котором была введена команда, показано в специальном блоке.
1778 Этот блок показывается только в том случае, если терминал
1779 текущей команды отличается от терминала предыдущей.
1780 </p></li>
1781 <li><p>
1782 Вывод не интересующих вас в настоящий момент элементов журнала,
1783 таких как время, имя терминала и других, можно отключить.
1784 Для этого нужно воспользоваться <a href='#visibility_form'>формой управления журналом</a>
1785 вверху страницы.
1786 </p></li>
1787 <li><p>
1788 Небольшие комментарии к командам можно вставлять прямо из командной строки.
1789 Комментарий вводится прямо в командную строку, после символов #^ или #v.
1790 Символы ^ и v показывают направление выбора команды, к которой относится комментарий:
1791 ^ - к предыдущей, v - к следующей.
1792 Например, если в командной строке было введено:
1793 <pre class='cline'>
1794 \$ whoami
1795 </pre>
1796 <pre class='output'>
1797 user
1798 </pre>
1799 <pre class='cline'>
1800 \$ #^ Интересно, кто я?
1801 </pre>
1802 в журнале это будет выглядеть так:
1804 <pre class='cline'>
1805 \$ whoami
1806 </pre>
1807 <pre class='output'>
1808 user
1809 </pre>
1810 <table class='note'><tr><td width='100%' class='note_text'>
1811 <tr> <td> Интересно, кто я?<br/> </td></tr></table>
1812 </p></li>
1813 <li><p>
1814 Если комментарий содержит несколько строк,
1815 его можно вставить в журнал следующим образом:
1816 <pre class='cline'>
1817 \$ whoami
1818 </pre>
1819 <pre class='output'>
1820 user
1821 </pre>
1822 <pre class='cline'>
1823 \$ cat > /dev/null #^ Интересно, кто я?
1824 </pre>
1825 <pre class='output'>
1826 Программа whoami выводит имя пользователя, под которым
1827 мы зарегистрировались в системе.
1829 Она не может ответить на вопрос о нашем назначении
1830 в этом мире.
1831 </pre>
1832 В журнале это будет выглядеть так:
1833 <table>
1834 <tr class='command'>
1835 <td class='script'>
1836 <pre class='cline'>
1837 \$ whoami</pre>
1838 <pre class='output'>user
1839 </pre>
1840 <table class='note'><tr><td class='note_title'>Интересно, кто я?</td></tr><tr><td width='100%' class='note_text'>
1841 Программа whoami выводит имя пользователя, под которым<br/>
1842 мы зарегистрировались в системе.<br/>
1843 <br/>
1844 Она не может ответить на вопрос о нашем назначении<br/>
1845 в этом мире.<br/>
1846 </td></tr></table>
1847 </td>
1848 </tr>
1849 </table>
1850 Для разделения нескольких абзацев между собой
1851 используйте символ "-", один в строке.
1852 <br/>
1853 </p></li>
1854 <li><p>
1855 Комментарии, не относящиеся непосредственно ни к какой из команд,
1856 добавляются точно таким же способом, только вместо симолов #^ или #v
1857 нужно использовать символы #=
1858 </p></li>
1860 <p><li>
1861 Содержимое файла может быть показано в журнале.
1862 Для этого его нужно вывести с помощью программы cat.
1863 Если вывод команды отметить симоволами #!,
1864 содержимое файла будет показано в журнале
1865 в специально отведённой для этого секции.
1866 </li></p>
1868 <p>
1869 <li>
1870 Для того чтобы вставить скриншот интересующего вас окна в журнал,
1871 нужно воспользоваться командой l3shot.
1872 После того как команда вызвана, нужно с помощью мыши выбрать окно, которое
1873 должно быть в журнале.
1874 </li>
1875 </p>
1877 <p>
1878 <li>
1879 Команды в журнале расположены в хронологическом порядке.
1880 Если две команды давались одна за другой, но на разных терминалах,
1881 в журнале они будут рядом, даже если они не имеют друг к другу никакого отношения.
1882 <pre>
1887 </pre>
1888 Группы команд, выполненных на разных терминалах, разделяются специальной линией.
1889 Под этой линией в правом углу показано имя терминала, на котором выполнялись команды.
1890 Для того чтобы посмотреть команды только одного сенса,
1891 нужно щёкнуть по этому названию.
1892 </li>
1893 </p>
1894 </ol>
1895 HELP
1897 $Html_About = <<ABOUT;
1898 <p>
1899 <a href='http://xgu.ru/lilalo/'>LiLaLo</a> (L3) расшифровывается как Live Lab Log.<br/>
1900 Программа разработана для повышения эффективности обучения Unix/Linux-системам.<br/>
1901 (c) Игорь Чубин, 2004-2008<br/>
1902 </p>
1903 ABOUT
1904 $Html_About.='$Id$ </p>';
1906 $Html_JavaScript = <<JS;
1907 function getElementsByClassName(Class_Name)
1909 var Result=new Array();
1910 var All_Elements=document.all || document.getElementsByTagName('*');
1911 for (i=0; i<All_Elements.length; i++)
1912 if (All_Elements[i].className==Class_Name)
1913 Result.push(All_Elements[i]);
1914 return Result;
1916 function ShowHide (name)
1918 elements=getElementsByClassName(name);
1919 for(i=0; i<elements.length; i++)
1920 if (elements[i].style.display == "none")
1921 elements[i].style.display = "";
1922 else
1923 elements[i].style.display = "none";
1924 //if (elements[i].style.visibility == "hidden")
1925 // elements[i].style.visibility = "visible";
1926 //else
1927 // elements[i].style.visibility = "hidden";
1929 function filter_by_output(text)
1932 var jjj=0;
1934 elements=getElementsByClassName('command');
1935 for(i=0; i<elements.length; i++) {
1936 subelems = elements[i].getElementsByTagName('pre');
1937 for(j=0; j<subelems.length; j++) {
1938 if (subelems[j].className = 'output') {
1939 var str = new String(subelems[j].nodeValue);
1940 if (jjj != 1) {
1941 alert(str);
1942 jjj=1;
1944 if (str.indexOf(text) >0)
1945 subelems[j].style.display = "none";
1946 else
1947 subelems[j].style.display = "";
1955 JS
1957 $SetCursorPosition_JS = <<JS;
1958 function setCursorPosition(oInput,oStart,oEnd) {
1959 oInput.focus();
1960 if( oInput.setSelectionRange ) {
1961 oInput.setSelectionRange(oStart,oEnd);
1962 } else if( oInput.createTextRange ) {
1963 var range = oInput.createTextRange();
1964 range.collapse(true);
1965 range.moveEnd('character',oEnd);
1966 range.moveStart('character',oStart);
1967 range.select();
1970 JS
1972 %Search_Machines = (
1973 "google" => { "query" => "http://www.google.com/search?q=" ,
1974 "icon" => "$Config{frontend_google_ico}" },
1975 "freebsd" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
1976 "icon" => "$Config{frontend_freebsd_ico}" },
1977 "linux" => { "query" => "http://man.he.net/?topic=",
1978 "icon" => "$Config{frontend_linux_ico}"},
1979 "opennet" => { "query" => "http://www.opennet.ru/search.shtml?words=",
1980 "icon" => "$Config{frontend_opennet_ico}"},
1981 "local" => { "query" => "http://www.freebsd.org/cgi/man.cgi?query=",
1982 "icon" => "$Config{frontend_local_ico}" },
1984 );
1986 %Elements_Visibility = (
1987 "0 new_commands_table" => "новые команды",
1988 "1 diff" => "редактор",
1989 "2 time" => "время",
1990 "3 ttychange" => "терминал",
1991 "4 wrong_output wrong_cline wrong_root_output wrong_root_cline"
1992 => "команды с ненулевым кодом завершения",
1993 "5 mistyped_output mistyped_cline mistyped_root_output mistyped_root_cline"
1994 => "неверно набранные команды",
1995 "6 interrupted_output interrupted_cline interrupted_root_output interrupted_root_cline"
1996 => "прерванные команды",
1997 "7 tab_completion_output tab_completion_cline"
1998 => "продолжение с помощью tab"
1999 );
2001 @Day_Name = qw/ Воскресенье Понедельник Вторник Среда Четверг Пятница Суббота /;
2002 @Month_Name = qw/ Январь Февраль Март Апрель Май Июнь Июль Август Сентябрь Октябрь Ноябрь Декабрь /;
2003 @Of_Month_Name = qw/ Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря /;
2009 # Временно удалённый код
2010 # Возможно, он не понадобится уже никогда
2013 sub search_by
2015 my $sm = shift;
2016 my $topic = shift;
2017 $topic =~ s/ /+/;
2019 return "<a href='". $Search_Machines{$sm}->{"query"}."$topic'><img width='16' height='16' src='".
2020 $Search_Machines{$sm}->{"icon"}."' border='0'/></a>";
2026 ########################################################################################
2028 # mywi
2039 sub mywi_init
2041 our $MyWiFile = "/home/igor/mywi/mywi.txt";
2042 our $MyWiLog = "/home/igor/mywi/mywi.log";
2043 our $section="";
2045 our @MywiTXT; # Массив текстовых записей mywi
2046 our %MywiHASH; # Хэш массивов записей
2047 our %Query;
2049 load_mywitxt($MyWiFile, \@MywiTXT, \%MywiHASH);
2052 sub mywi_process_query($)
2054 # Сделать подсказку по заданному запросу
2055 # $_[0] - тема для подсказки
2057 # Возвращает:
2058 # строку-подсказку
2061 my $query = shift;
2062 parse_query($query, \%Query);
2063 $result = search_in_txt(\%Query, \@MywiTXT, \%MywiHASH);
2065 if (!$result) {
2066 #add_to_log(\%Query, $MyWiLog);
2067 return "$query nothing appropriate. Logged. ".join (";",%Query);
2070 return $result;
2073 ####################################################################################
2074 # private section
2075 ####################################################################################
2077 sub load_mywitxt
2079 # Загрузить файл с записями Mywi_TXT
2080 # в массив
2081 # $_[0] - указатель на массив для загрузки
2082 # $_[1] - имя файла для загрузки
2085 my $MyWiFile = $_[0];
2086 my $MywiTXT = $_[1];
2087 my $MywiHASH = $_[2];
2089 open (MW, "$MyWiFile") or die "Can't open $MyWiFile for reading";
2090 binmode MW, ":utf8";
2091 @{$MywiTXT} = <MW>;
2092 close (MWF);
2094 for my $mywi_line (@{$MywiTXT}) {
2095 my $topic = $mywi_line;
2096 $topic =~ s@\s*\(.*\n@@;
2097 push @{$$MywiHASH{"$topic"}}, $mywi_line;
2098 # $MywiHASH{"$topic"} .= $mywi_line;
2102 sub parse_query
2104 # Строка запроса:
2105 # [format:]topic[(section)]
2106 # Элементы format и topic являются не обязательными
2108 # $_[0] - строка запроса
2109 # $_[1] - ссылка на хэш запроса
2112 my $query_string = shift;
2113 my $query_hash = shift;
2115 %{$query_hash} = (
2116 "format" => "txt",
2117 "section" => "",
2118 "topic" => "",
2119 );
2121 if ($query_string =~ s/^([^:]*)://) {
2122 $query_hash->{"format"} = $1 || "txt";
2124 if ($query_string =~ s/\(([^(]*)\)$//) {
2125 $query_hash->{"section"} = $1 || "";
2127 $query_hash->{"topic"} = $query_string;
2131 sub search_in_txt
2133 # Выполнить поиск в текстовой базе
2134 # по известному запросу
2135 # $_[0] -- ссылка на хэш запроса
2136 # $_[1] -- ссылка на массив текстовых записей
2137 # $_[2] -- ссылка на хэш массивов текстовых записей
2138 # Результат:
2139 # найденная текстовая запись в заданном формате
2142 my %Query = %{$_[0]};
2143 my %MywiHASH = %{$_[2]};
2145 my $topic = $Query{"topic"};
2146 my $section = $Query{"section"};
2147 my $result = "";
2149 return join("\n",@{$MywiHASH{"$topic"}})."\n";
2151 for my $l (@{$$_[2]{$topic}}) {
2152 # for my $l (@{$_[1]}) {
2153 my $line = $l;
2154 if (
2155 ($section and $line =~ /^\s*\Q$topic\E\s*\($section*\)\s*-/ )
2156 or (not $section and $line =~ /^\s*\Q$topic\E\s*(\([^)]*\)?)\s*-/) ) {
2157 $line =~ s/^.* -//mg if ($Config{"short"});
2158 $result .= "<para>$line</para>";
2161 return $result;
2165 sub add_to_log($$)
2167 # Если в базе отсутствует информация по данной теме,
2168 # сделать предположение доступным способом
2169 # и добавить его в базу
2170 # или просто сделать отметку о необходимости
2171 # расширения базы
2173 # Добавить запись в журнал
2174 # $_[0] - запись (ссылка на хэш)
2175 # $_[1] - имя файла-журнала
2178 my $query = $_[0];
2179 my $MyWiLog = $_[1];
2181 open (MWF, ">>:utf8", $MyWiLog) or die "Can't open $MyWiLog for writing";
2182 my $my_guess = mywi_guess($query);
2183 print MWF "$my_guess\n";
2184 close(MWF);
2187 sub mywi_guess($)
2188 # Сформировать исходную строку для журнала по заданному запросу
2189 # Если секция принадлежит 0..9, в качестве основы для результирующего текста использовать whatis
2190 # $_[0] - запись (ссылка на хэш)
2192 # Возвращает:
2193 # строку-предположение
2195 my %query = %{$_[0]};
2197 my $topic = $query{"topic"};
2198 my $section = $query{"section"};
2200 my $result = "$topic($section)";
2201 if (!$section or $section =~ /^[1-9]$/)
2203 # Запрос из категории 1-9
2204 # Об этом может знать whatis
2205 $result = `LANG=C whatis -- "$topic"`;
2206 if ($result =~ /nothing appropriate/i) {
2207 $result = $topic;
2208 $result .= "($section)" if $section;
2210 else {
2211 1 while ($result =~ s/(\s+)-(\s+)/$1+$2/sg);
2212 $result =~ s/\s+\(/(/;
2213 chomp $result;
2216 return $result;