lilalo

changeset 155:8ee5e59f1bd3 lilalo-sqlite

Локальное хранение и анализ данных с помощью SQlite

Очень много изменений, касающихся работы с sqlite
и локального использования результатов записи.
Подробнее:
README.l3text
author Igor Chubin <igor@chub.in>
date Tue Mar 16 20:05:30 2010 +0200 (2010-03-16)
parents 71d6b2a6b8eb
children a0daf0c3fa52
files README README.l3text l3-agent l3bashrc l3config.pm l3text
line diff
     1.1 --- a/README	Mon Dec 28 22:00:37 2009 +0300
     1.2 +++ b/README	Tue Mar 16 20:05:30 2010 +0200
     1.3 @@ -13,3 +13,4 @@
     1.4  
     1.5      http://xgu.ru/wiki/LiLaLo
     1.6  
     1.7 +
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/README.l3text	Tue Mar 16 20:05:30 2010 +0200
     2.3 @@ -0,0 +1,380 @@
     2.4 +
     2.5 +
     2.6 +l3text (или l3) это программа, которая предоставляет интерфейс 
     2.7 +к информации, записанной LiLaLo, прямо в командной строке.
     2.8 +
     2.9 +Результаты распознавания записываются с помощью l3-agent
    2.10 +в базу данных SQLite, и они становятся сразу же доступными для
    2.11 +использования с помощью l3text.
    2.12 +
    2.13 +l3text — это набор инструментов, для работы с транскриптами сеансов,
    2.14 +сгруппированные под одной крышей.
    2.15 +
    2.16 +Некоторые примеры использования.
    2.17 +
    2.18 +Показать весь журнал команд в текущем контексте
    2.19 +(начиная с текущего положения).
    2.20 +    $ l3text cat 
    2.21 +
    2.22 +Показать только командные строки или только вывод:
    2.23 +    $ l3text cat -c
    2.24 +    $ l3text cat -o
    2.25 +
    2.26 +Показать последние 10 команд:
    2.27 +    $ l3text tail
    2.28 +
    2.29 +Показать последние 20 команд:
    2.30 +    $ l3text tail -20
    2.31 +
    2.32 +Следить за текущим сеансом, то есть показывать все команды,
    2.33 +выполняющиеся в текущем контексте:
    2.34 +    $ l3text tail -f
    2.35 +
    2.36 +По умолчанию показываются только команды и результаты их выполнения.
    2.37 +Вывод можно настроить:
    2.38 +    $ l3 cat -o time,cline,output
    2.39 +
    2.40 +== Контекст ==
    2.41 +
    2.42 +Все команды, которые вы набираете, и результаты их выполнения
    2.43 +не повисают в воздухе, а дописываются в конец листов,
    2.44 +находящихся в определённых местах.
    2.45 +
    2.46 +Лист, на который попадают команды называется ''контекстом записи''.
    2.47 +
    2.48 +Вы можете набирать команды в разных окнах,
    2.49 +но если они находятся в одном контексте, то они запишутся
    2.50 +на один лист, одна под другой.
    2.51 +
    2.52 +С другой стороны, в одном окне вы можете поменять контекст несколько раз,
    2.53 +и команды, набиравшиеся в этих контекстах, попадут на разные листы.
    2.54 +
    2.55 +По умолчанию контекст равен /default , 
    2.56 +но для удобства дальнейшей работы с записями рекомендуется
    2.57 +задавать нужный контекст, которому соответствуют выполняемые действия.
    2.58 +
    2.59 +Сменить текущий контекст записи можно так:
    2.60 +    l3 context /path/to/new/context
    2.61 +
    2.62 +Просмотреть контекст записи можно так:
    2.63 +    l3 context
    2.64 +
    2.65 +Итак, контекст записи (контекст агента) определяет,
    2.66 +куда попадают команды при записи.
    2.67 +
    2.68 +Мы записывали команды не просто так.
    2.69 +Мы хотим поработать с тем, что мы записали.
    2.70 +Лист, на котором записаны команды, с которыми мы хотим
    2.71 +поработать, определяется ''контекстом чтения''.
    2.72 +
    2.73 +То есть он определяет, какие именно команды
    2.74 +из записанных ранее мы имеем в виду.
    2.75 +
    2.76 +По умолчанию текущий контекст чтения становится равным
    2.77 +текущему контексту записи.
    2.78 +
    2.79 +Смена текущего контекста чтения:
    2.80 +    $ l3 cd /path/to/new/context
    2.81 +или
    2.82 +    $ l3 go /path/to/new/context
    2.83 +
    2.84 +Просмотр доступных контекстов:
    2.85 +    $ l3 ls 
    2.86 +
    2.87 +Просмотреть имя текущего контекста чтения:
    2.88 +    $ l3 pwd
    2.89 +
    2.90 +Символы : и / являются в имени контекста специальными.
    2.91 +
    2.92 +Символ / формирует иерархию контекстов,
    2.93 +а символ : указывает координаты строки в контексте.
    2.94 +
    2.95 +Перейти на 3 команды назад:
    2.96 +    $ l3 go :-3
    2.97 +
    2.98 +Перейти в контекст /adm/lab1, в его самый конец (и находиться
    2.99 +всё время в конце, если он растёт, то есть делаются новые записи):
   2.100 +    $ l3 go /adm/lab1
   2.101 +
   2.102 +Перейти в контекст /adm/lab1, на первую команду grep:
   2.103 +    $ l3 go /adm/lab1:/grep/
   2.104 +
   2.105 +О том, как указывать координаты интересующих нас строк,
   2.106 +написано ниже, в разделе «Интервалы».
   2.107 +
   2.108 +
   2.109 +== Команда ==
   2.110 +
   2.111 +    l3 [ОПЦИИ1] [ИНТЕРВАЛ] [filter ВЫРАЖЕНИЕ] [grep РЕГВЫР] [КОМАНДА] [ОПЦИИ2]
   2.112 +
   2.113 +== Интервалы ==
   2.114 +
   2.115 +По умолчанию операция выполняется над всеми командами,
   2.116 +находящимися в текущем контексте, начиная с текущей точки и до его конца.
   2.117 +
   2.118 +Можно ограничить команды с помощью интервала:
   2.119 +
   2.120 +    выражение
   2.121 +    выражение,выражение
   2.122 +
   2.123 +Примеры:
   2.124 +
   2.125 +    -10,.   последние 10 команд
   2.126 +    .,+3    от текущей команды три вниз
   2.127 +    /grep/  последняя команда grep
   2.128 +    o/grep/ последняя команда, содержащая grep в выводе (output)
   2.129 +    .       текущая строка
   2.130 +    @12:23  команда, выполненная в 12:23 (12:23 текущего часа)
   2.131 +    @08:12:00 команда, выполненная в 8:12:00 (или ближайшая после этого времени)
   2.132 +    1       первая команда в контексте
   2.133 +    $       последняя команда в контексте
   2.134 +    %       весь контекст
   2.135 +
   2.136 +
   2.137 +Интервал указывается перед командой, работу которой он ограничивает:
   2.138 +
   2.139 +    l3 -10,. cat 
   2.140 +напечатать последние 10 команд
   2.141 +
   2.142 +равносильно этому:
   2.143 +    l3 tail -10
   2.144 +
   2.145 +интервал = строка
   2.146 +интервал = строка,строка
   2.147 +интервал = %
   2.148 +строка   = число
   2.149 +         = -число
   2.150 +         = +число
   2.151 +         = @время
   2.152 +         = /регвыр/
   2.153 +         = .
   2.154 +         = $
   2.155 +
   2.156 +== Фильтрация ==
   2.157 +
   2.158 +Можно указать дополнительный фильтр,
   2.159 +который оставляет команды, которые нас интересуют.
   2.160 +
   2.161 +Есть два вида фильтров:
   2.162 +    * по регулярным выражениям
   2.163 +    * по вычисляемому выражению (синтаксис perl с некоторыми отличиями)
   2.164 +
   2.165 +grep -v regexp       инверсия
   2.166 +grep    regexp       прямой поиск
   2.167 +
   2.168 +-f полный
   2.169 +-o только в выводе
   2.170 +-c только в команде (по умолчанию)
   2.171 +
   2.172 +filter выражение
   2.173 +
   2.174 +== Команды ==
   2.175 +
   2.176 +cat
   2.177 +tail
   2.178 +head
   2.179 +history
   2.180 +
   2.181 +Все эти команды являются сокращением для одной команды.
   2.182 +
   2.183 +== Зачем всё это нужно ==
   2.184 +
   2.185 +* использование данных вывода команд при автоматическом продолжении команд
   2.186 +* быстрое выделение и копирование команд
   2.187 +* поиск команд
   2.188 +* привязка ко времени
   2.189 +* подключение к журналу
   2.190 +
   2.191 +=== Tab-completion: Данные вывода команд при наборе других команд ===
   2.192 +
   2.193 +Вы выполнили одну команду, 
   2.194 +и хотите использовать результаты её исполнения
   2.195 +в качестве аргумента другой.
   2.196 +
   2.197 +Например, вы просканировали wifi-сети:
   2.198 +    # iwlist scanning
   2.199 +В выводе команды присутствовали названия SSID, один из которых мы будем использовать дальше.
   2.200 +Было бы классно, если бы дальше, когда мы вводим команду
   2.201 +    # iwconfig wlan0 essid ____
   2.202 +мы могли использовать табуляцию при наборе essid.
   2.203 +
   2.204 +В качестве списка возможных продолжений
   2.205 +должны использоваться сети, которые выдала предыдущая команда.
   2.206 +
   2.207 +Это становится возможным с использованием l3text
   2.208 +(при написании соответствующих расширения для bash_completion).
   2.209 +
   2.210 +=== Быстрое выделение и копирование команд ===
   2.211 +
   2.212 +Ваш друг помогает вам настроить систему по джабберу.
   2.213 +Он присылает вам команды, которые вы творчески перерабатываете
   2.214 +и вводите в консоль.
   2.215 +
   2.216 +Потом, для того чтобы он видел, что получается, вы копируете результаты
   2.217 +исполнения ему в джаббер.
   2.218 +
   2.219 +Например, вы хотите показать результат пяти последних команд:
   2.220 +
   2.221 +В самом простейшем случае (если вы используете графический-джаббер клиент)
   2.222 +    l3 tail -5 | xsel
   2.223 +
   2.224 +Потом идёте в джаббер-клиент и делаете:
   2.225 +    shift-insert
   2.226 +
   2.227 +Можно отправить ему код прямо из консоли:
   2.228 +    l3 tail -5 | sendxmpp igor@chub.in
   2.229 +
   2.230 +Или даже ещё проще, всё, что вы делаете будет копироваться ему:
   2.231 +    l3 tail -f | sendxmpp igor@chub.in
   2.232 +
   2.233 +Или, если вы не хотите, чтобы копировались команды, набранные неверно:
   2.234 +    l3 filter err!=128 tail -f | sendxmpp igor@chub.in
   2.235 +
   2.236 +
   2.237 +Команды l3 не обязательно вызывать в той сессии, где идёт настройка.
   2.238 +Вы можете их вызывать в соседнем терминале, где вообще не идёт запись.
   2.239 +Конечно, вы должны будете указать соответствующий контекст.
   2.240 +
   2.241 +    l3 cd /adm/lab1
   2.242 +
   2.243 +(предполагается, что настройка идёт в /adm/lab1 )
   2.244 +
   2.245 +=== Поиск команд ===
   2.246 +
   2.247 +Вы выполняете настройку сервера,
   2.248 +в ходе которой сделали много инсталляций пакетов.
   2.249 +
   2.250 +Вы делали их в разных окнах, уже сами не помните в каких,
   2.251 +но запись всё время шла.
   2.252 +
   2.253 +Вы хотите увидеть имена пакетов, которые были установлены.
   2.254 +
   2.255 +Простейший вариант:
   2.256 +
   2.257 +    l3 grep apt-get
   2.258 +
   2.259 +Если вы помните, что начали работу по инсталляции после обеда,
   2.260 +а всё, что было до этого, к делу не относится:
   2.261 +    l3 @14:00:00,$ grep apt-get 
   2.262 +
   2.263 +Если вы помните, что было много инсталляций, но в некоторых из них
   2.264 +вы неправильно указывали имена пакетов, и вы хотели бы оставить
   2.265 +только те из них, которые выполнились успешно:
   2.266 +    l3 grep err=0 grep apt-get
   2.267 +или
   2.268 +    l3 filter err=0&&/apt-get/
   2.269 +или
   2.270 +    l3 filter err=0 grep apt-get 
   2.271 +
   2.272 +=== Привязка ко времени ===
   2.273 +
   2.274 +Вы рассказываете, как настроить какую-то вещь.
   2.275 +Вы хотели бы, чтобы всё, что вы рассказываете,
   2.276 +можно было использовать как основу для составления
   2.277 +более подробного документа.
   2.278 +
   2.279 +Вы используете доску (или графический планшет),
   2.280 +некоторые вещи делаются не в командной строке,
   2.281 +а на экране с графическим интерфейсом,
   2.282 +кроме того ваш голос записывается.
   2.283 +
   2.284 +Вы не можете тратить время на то, чтобы положить скриншоты
   2.285 +в нужное место, но вы можете их сделать.
   2.286 +Аналогично и со скринкастами. Вы можете записывать то,
   2.287 +что вы делаете при подключении к какой-то системе,
   2.288 +но вы не можете выполнять никаких других операций
   2.289 +по обработке записи. Потому что на это просто нет времени.
   2.290 +
   2.291 +Ход вашей работы фиксируется, операции запоминаются,
   2.292 +скриншоты помечаются временными метками.
   2.293 +Аналогично происходит со схемами на доске,
   2.294 +которые фотографируются и представляются в виде помеченных
   2.295 +временными метками файлов.
   2.296 +
   2.297 +После того, как сеанс окончен,
   2.298 +вы получаете его транскрипт, который можно дальше редактировать.
   2.299 +Команды в этом транскрипте сопровождаются специальными якорями,
   2.300 +с помощью которых они привязываются к записанному ранее сеансу.
   2.301 +
   2.302 +В транскрипте вы удаляете лишние команды,
   2.303 +добавляете текст и получаете документ,
   2.304 +который подробно описывает происходившее,
   2.305 +причём при желании вы можете получить недостающую информацию
   2.306 +из архива сделанных ранее операций.
   2.307 +
   2.308 +Туда же, в нужные (соответствующие времени) места, 
   2.309 +автоматически попадают сделанные скриншоты,
   2.310 +скринкасты, фотографии, запись звука.
   2.311 +
   2.312 +
   2.313 +=== Подключение к журналу ===
   2.314 +
   2.315 +Продолжим предыдущий пример.
   2.316 +
   2.317 +Вы прочитали документацию, составленную по описанной выше методике,
   2.318 +и теперь хотите выполнить такую же настройку.
   2.319 +
   2.320 +В обычном случае вы просто копируете команды в свою командную строку
   2.321 +или набираете их заново, с учётом ваших собственных условий.
   2.322 +
   2.323 +Можно значительно ускорить и упростить этот процесс,
   2.324 +а так же уменьшить вероятность ошибки возможной в ходе его выполнения.
   2.325 +
   2.326 +Вы подключаетесь к сделанной записи:
   2.327 +    l3 go http://xgu.ru/l3/option-82
   2.328 +
   2.329 +Все рассмотренные ранее команды становятся доступным.
   2.330 +
   2.331 +Становится доступным продолжение команд (tab completion)
   2.332 +на основе информации, набранной в той работе
   2.333 +и даже больше: вы можете добавить команды
   2.334 +из контекста в историю текущего командного интерпретатора,
   2.335 +как будто вы их сами уже выполняли.
   2.336 +
   2.337 +Автоматически они не добавляются, чтобы не создавать путаницы.
   2.338 +Но вы можете их загрузить:
   2.339 +    l3 % | l3hist
   2.340 +или, если вас интересуют только определённые команды, то с использование интервала,
   2.341 +например:
   2.342 +    l3 @14:00,$ | l3hist
   2.343 +
   2.344 +Вам тогда не нужно будет их заново набирать.
   2.345 +Достаточно прокрутить историю вверх, а потом просто выполнять 
   2.346 +по одной, нажимая ctrl-o, при этом адаптируя к своим условиям.
   2.347 +
   2.348 +Чтобы ещё сильнее сократить себе объём работы,
   2.349 +можно применить процедуру адаптации, которая позволяет
   2.350 +автоматически заменять специфические для процедуры параметры.
   2.351 +
   2.352 +Для этого вы вызываете:
   2.353 +    l3 parameterize
   2.354 +    l3 param  (сокращённо)
   2.355 +которая открывает текстовый редактор, где указывается два столбика параметров:
   2.356 +что заменить и на что заменить.
   2.357 +
   2.358 +Например, пусть в описанном документе используются адреса:
   2.359 +    192.168.15.1
   2.360 +    192.168.15.254
   2.361 +    example.com
   2.362 +
   2.363 +Причём составитель этого документа мог указать, что эти параметры являются специфичными,
   2.364 +а мог не указывать. Если он указал, то совсем хорошо — тогда вам нужно просто пройтись
   2.365 +и добавить справа, на что они должны заменяться в вашем случае.
   2.366 +Если же он не указал, то вам нужно придумать список замен по своему вкусу.
   2.367 +
   2.368 +Вы вводите локализованную версию:
   2.369 +    192.168.15.1        10.0.35.1
   2.370 +    192.168.15.254      10.0.35.254
   2.371 +    example.com         xgu.ru
   2.372 +
   2.373 +Теперь, во всех выводах команды l3 будут исправленные команды
   2.374 +и исправленные результаты их выполнения.
   2.375 +Автопродолжение (tab completion) в шелле уже будет показывать исправленные аргументы.
   2.376 +Историю командного интерпретатора вы тоже можете обновить,
   2.377 +и в ней тогда будут изменённые команды.
   2.378 +
   2.379 +Использовать параметризацию с загрузкой удалённого контекста
   2.380 +настолько логично, что вы можете это сделать одной командой:
   2.381 +    l3 go -p http://xgu.ru/l3/option-82
   2.382 +
   2.383 +
     3.1 --- a/l3-agent	Mon Dec 28 22:00:37 2009 +0300
     3.2 +++ b/l3-agent	Tue Mar 16 20:05:30 2010 +0200
     3.3 @@ -10,6 +10,7 @@
     3.4  use Text::Iconv;
     3.5  use Time::Local 'timelocal_nocheck';
     3.6  use IO::Socket;
     3.7 +use DBI;
     3.8  
     3.9  use lib "/etc/lilalo";
    3.10  use l3config;
    3.11 @@ -176,8 +177,7 @@
    3.12  {
    3.13      my $cline = $_[0];
    3.14      my @lists = split /\;/, $cline;
    3.15 -    
    3.16 -    
    3.17 +
    3.18      my @commands = ();
    3.19      for my $list (@lists) {
    3.20          push @commands, split /\|/, $list;
    3.21 @@ -344,7 +344,7 @@
    3.22          my $tty = $1;
    3.23          my %cl;
    3.24          my $last_output_length=0;
    3.25 -	my $saved_output;
    3.26 +        my $saved_output;
    3.27          while (<FILE>) {
    3.28              $commandlines_processed++;
    3.29  
    3.30 @@ -357,7 +357,8 @@
    3.31                  $commandlines_loaded++;
    3.32                  $last_output_length=0;
    3.33  
    3.34 -                # Previous command
    3.35 +                # Сохраняем часть выполненного ранее разбора в переменной last_cl,
    3.36 +                # которую мы дополним некоторой информацией и сохраним позже
    3.37                  my %last_cl = %cl;
    3.38                  my $this_line = $1;
    3.39                  my $err = $2 || "";
    3.40 @@ -425,7 +426,6 @@
    3.41                  $last_cl{"err"}=$err;
    3.42                  $last_cl{"err"}=130 if $err eq "^C";
    3.43  
    3.44 -
    3.45                  # Output
    3.46                  if (!$last_cl{"suppress_output"} || $last_cl{"err"}) {
    3.47                      for (my $i=0; $i<$Config{"terminal_height"}; $i++) {
    3.48 @@ -570,8 +570,19 @@
    3.49                  }
    3.50  
    3.51                  $vt{$local_session_id}->reset();
    3.52 -		$saved_output="";
    3.53 +                $saved_output="";
    3.54  
    3.55 +# Обработка команд с одинаковым временем
    3.56 +# Скорее всего они набраны с помощью tab-completion
    3.57 +                if ((defined($last_cl{time}) 
    3.58 +                     && defined($cl{time})
    3.59 +                     && $last_cl{time} == $cl{time}) 
    3.60 +                    && 
    3.61 +                    (defined($last_cl{nonce}) 
    3.62 +                     && defined($cl{nonce}) 
    3.63 +                     && $last_cl{nonce} == $cl{nonce})) {
    3.64 +                    $last_cl{"tab"}=1;
    3.65 +                };
    3.66  
    3.67                  # Changing encoding 
    3.68                  for (keys %last_cl) {
    3.69 @@ -675,21 +686,109 @@
    3.70      $text =~ s/&/&amp;/g;
    3.71      $text =~ s/</&lt;/g;
    3.72      $text =~ s/>/&gt;/g;
    3.73 +    $text =~ s/"/&quot;/g;
    3.74      print $TO $text;
    3.75  }
    3.76  
    3.77 +sub strq
    3.78 +{
    3.79 +    my $text = join "", @_;
    3.80 +    $text =~ s/&/&amp;/g;
    3.81 +    $text =~ s/</&lt;/g;
    3.82 +    $text =~ s/>/&gt;/g;
    3.83 +    $text =~ s/"/&quot;/g;
    3.84 +    return $text;
    3.85 +}
    3.86  
    3.87  =cut 
    3.88  Вывести результат обработки журнала.
    3.89  =cut
    3.90  
    3.91 +sub print_commands_to_sqlite_cache
    3.92 +{
    3.93 +    my $output_filename=$_[0];
    3.94 +    my $db = DBI->connect("dbi:SQLite:$output_filename", "", "",
    3.95 +                          {RaiseError => 1, AutoCommit => 1});
    3.96 +
    3.97 +    my $cl;
    3.98 +    for my $i (@Command_Lines_Index) {
    3.99 +        my $val;
   3.100 +        my $var_list;
   3.101 +        my $values_list;
   3.102 +        my $create_var_list;
   3.103 +        my $sql;
   3.104 +        $cl = $Command_Lines[$i];
   3.105 +
   3.106 +        $cl->{l3cd}=$Config{l3cd} if $Config{"l3cd"};
   3.107 +        for my $element (qw(
   3.108 +            local_session_id
   3.109 +            history
   3.110 +            uid
   3.111 +            pid
   3.112 +            time
   3.113 +            pwd
   3.114 +            raw_start
   3.115 +            raw_output_start
   3.116 +            raw_end
   3.117 +            raw_file
   3.118 +            tty
   3.119 +            err
   3.120 +            last_command
   3.121 +            nonce
   3.122 +            l3cd
   3.123 +            tab
   3.124 +            )) {
   3.125 +            if (defined($cl->{"$element"})) {
   3.126 +                $val = $cl->{"$element"};
   3.127 +            }
   3.128 +            else {
   3.129 +                $val = ""
   3.130 +            };
   3.131 +            $var_list .= "$element,";
   3.132 +            $values_list .= '"'.$val."\",";
   3.133 +            $create_var_list .= ", $element TEXT";
   3.134 +        }
   3.135 +        for my $element (qw(
   3.136 +            prompt
   3.137 +            cline
   3.138 +            output
   3.139 +            )) {
   3.140 +            if (defined($cl->{"$element"})) {
   3.141 +                $val = $cl->{"$element"};
   3.142 +            }
   3.143 +            else {
   3.144 +                $val = ""
   3.145 +            };
   3.146 +            $var_list .= "$element,";
   3.147 +            $values_list .= '"'.strq($val)."\",";
   3.148 +            $create_var_list .= ", $element TEXT";
   3.149 +        }
   3.150 +        if ($cl->{"diff"}) {
   3.151 +            $val = strq(${$Diffs{$cl->{"diff"}}}{"text"});
   3.152 +        }
   3.153 +        else {
   3.154 +            $val="";
   3.155 +        }
   3.156 +        $var_list .= "diff,";
   3.157 +        $create_var_list .= ", diff TEXT";
   3.158 +        $values_list .= '"'.$val."\",";
   3.159 +
   3.160 +        $db->do("CREATE TABLE IF NOT EXISTS commands (id INTEGER PRIMARY KEY $create_var_list)");
   3.161 +        #print "CREATE TABLE commands (id INTEGER PRIMARY KEY $create_var_list)\n";
   3.162 +        $values_list =~ s/,$//;
   3.163 +        $var_list =~ s/,$//;
   3.164 +        $sql = "INSERT INTO commands ($var_list) VALUES($values_list) ";
   3.165 +        print "$sql\n";
   3.166 +        $db->do($sql);
   3.167 +    }
   3.168 +}
   3.169 +
   3.170  sub print_command_lines
   3.171  {
   3.172      my $output_filename=$_[0];
   3.173      open(OUT, ">>", $output_filename)
   3.174          or die "Can't open $output_filename for writing\n";
   3.175  
   3.176 -
   3.177      my $cl;
   3.178      my $in_range=0;
   3.179      for my $i (@Command_Lines_Index) {
   3.180 @@ -704,17 +803,12 @@
   3.181              next;
   3.182          }
   3.183          next if ($Config{"from"} && $Config{"to"} && !$in_range) 
   3.184 -            ||
   3.185 -                ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
   3.186 -            ||
   3.187 -            ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
   3.188 -            ||
   3.189 -            ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
   3.190 -        
   3.191 +             || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ )
   3.192 +             || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0)
   3.193 +             || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
   3.194 +
   3.195          # Вырезаем из вывода только нужное количество строк
   3.196 -
   3.197          my $output="";
   3.198 -
   3.199          if (!grep ($_ eq $cl->{"last_command"}, @{$Config{"full_output_commands"}})
   3.200              && ($Config{"head_lines"} 
   3.201              || $Config{"tail_lines"})) { 
   3.202 @@ -730,20 +824,20 @@
   3.203              if ($start < 0) {
   3.204                  $start=0;
   3.205                  $mark=0;
   3.206 -            }   
   3.207 +            }
   3.208              if ($start < $Config{"cache_head_lines"}) {
   3.209                  $start=$Config{"cache_head_lines"};
   3.210                  $mark=0;
   3.211 -            }   
   3.212 +            }
   3.213              $output .= $Config{"skip_text"}."\n" if $mark;
   3.214              for ($i=$start; $i<= $#lines; $i++) {
   3.215                  $output .= $lines[$i]."\n";
   3.216              }
   3.217 -        } 
   3.218 +        }
   3.219          else {
   3.220              # Full output
   3.221              $output .= $cl->{"output"};
   3.222 -        }   
   3.223 +        }
   3.224  
   3.225          # Совместимость с labmaker
   3.226  
   3.227 @@ -759,7 +853,6 @@
   3.228          # timelocal(            $sec,      $min,      $hour,      $mday,$mon,$year);
   3.229          $cl->{time} ||= timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year);
   3.230  
   3.231 -
   3.232          # Начинаем вывод команды
   3.233          print OUT "<command>\n";
   3.234          print OUT "<l3cd>$Config{l3cd}</l3cd>\n" if $Config{"l3cd"};
   3.235 @@ -917,6 +1010,7 @@
   3.236          load_command_lines($Config{"input"}, $Config{"input_mask"});
   3.237          sort_command_lines;
   3.238          #process_command_lines;
   3.239 +        print_commands_to_sqlite_cache($Config{"cache_sqlite"});
   3.240          print_command_lines($Config{"cache"});
   3.241      } 
   3.242      else {
   3.243 @@ -975,6 +1069,7 @@
   3.244              if (@Command_Lines) {
   3.245                  sort_command_lines;
   3.246                  #process_command_lines;
   3.247 +                print_commands_to_sqlite_cache($Config{"cache_sqlite"});
   3.248                  print_command_lines($Config{"cache"});
   3.249              }
   3.250              save_cache_stat();
     4.1 --- a/l3bashrc	Mon Dec 28 22:00:37 2009 +0300
     4.2 +++ b/l3bashrc	Tue Mar 16 20:05:30 2010 +0200
     4.3 @@ -57,6 +57,7 @@
     4.4  {
     4.5      export L3_SESSION_ID=${RANDOM}${RANDOM}${RANDOM}${RANDOM}-`date +%s`
     4.6      export L3_HOME=~/.lilalo/
     4.7 +    L3_SQLITE=$L3_HOME/report.sqlite
     4.8      mkdir -p $L3_HOME
     4.9  
    4.10      tty=`/usr/bin/tty`
    4.11 @@ -70,6 +71,21 @@
    4.12      hostname=`hostname -f 2> /dev/null`
    4.13      [ -n "$bsd" ] && hostname=`hostname`
    4.14  
    4.15 +    cat <<INFO | perl
    4.16 +use DBI;
    4.17 +use strict;
    4.18 +my \$db = DBI->connect("dbi:SQLite:$L3_SQLITE", "", "",
    4.19 +{RaiseError => 1, AutoCommit => 1});
    4.20 +
    4.21 +\$db->do("CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY, 
    4.22 +            session TEXT, hostname TEXT, user TEXT, uid TEXT, login_from TEXT, 
    4.23 +            tty TEXT, system TEXT, parent TEXT, ppid TEXT, pid TEXT, start_time TEXT, lang TEXT)");
    4.24 +\$db->do("INSERT INTO sessions VALUES (NULL, '$L3_SESSION_ID', '$hostname', '$USER', '$UID', '$login_from',
    4.25 +            '$tty', '$system', '$parent', '$PPID', '$$',
    4.26 +            '$start_time', '$LANG')");
    4.27 +INFO
    4.28 +    #perl $temp_l3_name #; rm $temp_l3_name; unset temp_l3_name
    4.29 +
    4.30      cat <<INFO > $L3_HOME/$L3_SESSION_ID.info
    4.31  <session>
    4.32      <local_session_id>$L3_SESSION_ID</local_session_id>
    4.33 @@ -258,11 +274,14 @@
    4.34  l3 ()
    4.35  {
    4.36      case "$1" in
    4.37 -        on)
    4.38 -            echo switching on
    4.39 -        ;;
    4.40 -        off)
    4.41 -            echo switcing off
    4.42 +        context)
    4.43 +            if [ -z "$2" ] 
    4.44 +            then
    4.45 +                echo "$L3_CONTEXT"
    4.46 +            else
    4.47 +                echo $2 | grep -q ^/ && L3_CONTEXT="$2" || L3_CONTEXT="$L3_CONTEXT/$2"
    4.48 +                export L3_CONTEXT="`echo $L3_CONTEXT | perl -e '$_=<>; 1 while s@/[^/]*/\.\.@@; print;'`"
    4.49 +            fi
    4.50          ;;
    4.51          cd)
    4.52              echo l3cd="$2" > ~/.l3rc
    4.53 @@ -271,26 +290,10 @@
    4.54              grep ^l3cd= ~/.l3rc | sed s/[^=]*=//
    4.55          ;;
    4.56          *) 
    4.57 -            cat <<EOF
    4.58 -l3 [command]:
    4.59 -
    4.60 -    cd new_context   - change current lilalo context
    4.61 -    pwd              - show current lilalo context
    4.62 -    on               - switch writing on (NOT IMPLEMENTED YET)
    4.63 -    off              - switch writing off (NOT IMPLEMENTED YET)
    4.64 -
    4.65 -EOF
    4.66 +            l3text "$@"
    4.67          ;;
    4.68      esac
    4.69  }
    4.70  
    4.71 -l3cd()
    4.72 -{
    4.73 -    l3 cd "$@"
    4.74 -}
    4.75  
    4.76 -l3pwd()
    4.77 -{
    4.78 -    l3 pwd
    4.79 -}
    4.80  
     5.1 --- a/l3config.pm	Mon Dec 28 22:00:37 2009 +0300
     5.2 +++ b/l3config.pm	Tue Mar 16 20:05:30 2010 +0200
     5.3 @@ -6,7 +6,7 @@
     5.4  use Getopt::Long;
     5.5  
     5.6  @ISA = ('Exporter');
     5.7 -@EXPORT = qw(%Config &init_config);
     5.8 +@EXPORT = qw(%Config &init_config &init_config_without_command_line_processing);
     5.9  
    5.10  our $System_Config_File = "/etc/lilalo.conf";
    5.11  our $User_Config_File = "$ENV{HOME}/.l3rc";
    5.12 @@ -49,6 +49,7 @@
    5.13      "encoding"          => "utf-8",
    5.14  
    5.15      "cache"             => "$ENV{HOME}/.lilalo/report.xml",
    5.16 +    "cache_sqlite"      => "$ENV{HOME}/.lilalo/report.sqlite",
    5.17      "cache_stat"        => "$ENV{HOME}/.lilalo/.report.dat",
    5.18  
    5.19      "output"            => "/tmp/report.html",
    5.20 @@ -152,32 +153,44 @@
    5.21  
    5.22  sub read_config_file
    5.23  {
    5.24 -	my $config = $_[0];
    5.25 -	my $filename = $_[1];
    5.26 -	open(CONFIG, "$filename")
    5.27 -		or return;
    5.28 -	while (<CONFIG>) {
    5.29 +    my $config = $_[0];
    5.30 +    my $filename = $_[1];
    5.31 +    open(CONFIG, "$filename")
    5.32 +        or return;
    5.33 +    while (<CONFIG>) {
    5.34          chomp;
    5.35 -		s/#.*//;
    5.36 -		next if /^\s*$/;
    5.37 -		my ($var, $val) =  split /\s*=\s*/, $_, 2;
    5.38 -		$var =~ s/\s*//;
    5.39 -		$config->{$var} = $val;
    5.40 -	}
    5.41 -	close(CONFIG);
    5.42 +        s/#.*//;
    5.43 +        next if /^\s*$/;
    5.44 +        my ($var, $val) =  split /\s*=\s*/, $_, 2;
    5.45 +        $var =~ s/\s*//;
    5.46 +        $config->{$var} = $val;
    5.47 +    }
    5.48 +    close(CONFIG);
    5.49  }
    5.50  
    5.51  
    5.52  sub init_config
    5.53  {
    5.54 -	my %argv_config;
    5.55 -	my %file_config;
    5.56 -	read_config_file(\%file_config, $System_Config_File);
    5.57 -	read_config_file(\%file_config, $User_Config_File);
    5.58 -	GetOptions(\%argv_config, map "$_=s", keys %Config);
    5.59 -	%Config = (%Config, %file_config, %argv_config);
    5.60 +    my %argv_config;
    5.61 +    my %file_config;
    5.62 +    read_config_file(\%file_config, $System_Config_File);
    5.63 +    read_config_file(\%file_config, $User_Config_File);
    5.64 +    GetOptions(\%argv_config, map "$_=s", keys %Config);
    5.65 +    %Config = (%Config, %file_config, %argv_config);
    5.66      for my $key (keys %Config) {
    5.67          utf8::decode($Config{$key});
    5.68      }
    5.69  }
    5.70  
    5.71 +sub init_config_without_command_line_processing
    5.72 +{
    5.73 +    my %argv_config;
    5.74 +    my %file_config;
    5.75 +    read_config_file(\%file_config, $System_Config_File);
    5.76 +    read_config_file(\%file_config, $User_Config_File);
    5.77 +    %Config = (%Config, %file_config);
    5.78 +    for my $key (keys %Config) {
    5.79 +        utf8::decode($Config{$key});
    5.80 +    }
    5.81 +}
    5.82 +
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/l3text	Tue Mar 16 20:05:30 2010 +0200
     6.3 @@ -0,0 +1,365 @@
     6.4 +#!/usr/bin/perl -w
     6.5 +
     6.6 +use POSIX qw(strftime);
     6.7 +use lib '/etc/lilalo';
     6.8 +use l3config;
     6.9 +use utf8;
    6.10 +use DBI;
    6.11 +#no warnings;
    6.12 +
    6.13 +our @Fields=qw/id time prompt cline output err last_command tab diff l3cd/;
    6.14 +our $Fields=join(", ",@Fields);
    6.15 +our $db;
    6.16 +
    6.17 +=cut
    6.18 +Множество команд, с которыми выполняется операция,
    6.19 +определяется текущим контекстом и текущей строкой,
    6.20 +а также параметрами:
    6.21 +    * фильтр;
    6.22 +    * регулярное выражение;
    6.23 +    * интервал.
    6.24 +=cut 
    6.25 +our $Filter;
    6.26 +our $Grep;
    6.27 +our $Interval;
    6.28 +our $Command;
    6.29 +
    6.30 +# Если мы используем режим -f, то в этой переменной запоминается
    6.31 +# до какой точки мы дошли
    6.32 +our $Follow=0;
    6.33 +
    6.34 +our $Context;
    6.35 +
    6.36 +our @Commands = qw/head tail cat history/;
    6.37 +
    6.38 +sub main();
    6.39 +sub connect_database();
    6.40 +
    6.41 +main();
    6.42 +
    6.43 +sub main()
    6.44 +{
    6.45 +    $| = 1;
    6.46 +
    6.47 +    init_config_without_command_line_processing();
    6.48 +    $Context=$Config{l3cd};
    6.49 +    connect_database();
    6.50 +# Обработка всех grep'ов
    6.51 +    $Grep="";
    6.52 +    my $i=0;
    6.53 +    my $arg;
    6.54 +    my @argv=();
    6.55 +    while ($i<=$#ARGV) {
    6.56 +        $arg=$ARGV[$i];
    6.57 +        #print "arg=$arg\n";
    6.58 +        if ($arg eq "grep") {
    6.59 +            my $grep_keys="";
    6.60 +            my $grep_regexp="";
    6.61 +            $i++;
    6.62 +            while(defined ($ARGV[$i]) and $ARGV[$i] =~ /^-/) {
    6.63 +                $grep_keys .= " ".$ARGV[$i];
    6.64 +                $i++;
    6.65 +            }
    6.66 +            if (defined($ARGV[$i])) {
    6.67 +                $grep_regexp = $ARGV[$i];
    6.68 +                $i++;
    6.69 +            }
    6.70 +            else {
    6.71 +                die "ERROR: grep argument is not specified";
    6.72 +            }
    6.73 +            $Grep .= grep_options($grep_regexp,$grep_keys);
    6.74 +        }
    6.75 +        else {
    6.76 +            $i++;
    6.77 +            push(@new_argv,$arg);
    6.78 +        }
    6.79 +    }
    6.80 +    @argv=@new_argv;
    6.81 +
    6.82 +    if (grep(/^-f$/, @argv)) {
    6.83 +        $follow=1;
    6.84 +    }
    6.85 +    if (grep(/^history$/, @argv)) {
    6.86 +        my $interval=$argv[0]||"%";
    6.87 +        $interval="%" if $interval eq "history";
    6.88 +        my $exit=0;
    6.89 +        while (not $exit) {
    6.90 +            print_commands(select_interval($interval, $Grep));
    6.91 +            if (not $follow) {
    6.92 +                $exit=1;
    6.93 +            } else {
    6.94 +                sleep(1);
    6.95 +            }
    6.96 +        }
    6.97 +    } else {
    6.98 +        my $exit=0;
    6.99 +        while (not $exit) {
   6.100 +            print_all(select_interval($argv[0]||"%", $Grep));
   6.101 +            if (not $follow) {
   6.102 +                $exit=1;
   6.103 +            } else {
   6.104 +                sleep(1);
   6.105 +            }
   6.106 +        }
   6.107 +    }
   6.108 +    #print_all(select_by_cline("*privet*"));
   6.109 +}
   6.110 +
   6.111 +sub connect_database()
   6.112 +{
   6.113 +    $db = DBI->connect("dbi:SQLite:".$Config{cache_sqlite}, "", "",
   6.114 +                {RaiseError => 1, AutoCommit => 1});
   6.115 +    $db->func('regexp', 2, sub {
   6.116 +            my ($regex, $string) = @_;
   6.117 +                return $string =~ /$regex/;
   6.118 +    }, 'create_function');
   6.119 +}
   6.120 +
   6.121 +sub select_all() {
   6.122 +    $l3cd=$Config{l3cd};
   6.123 +    return $db->selectall_arrayref("SELECT $Fields FROM commands WHERE l3cd='$l3cd'");
   6.124 +}
   6.125 +
   6.126 +sub select_last_n($) {
   6.127 +    my $last=$_[0];
   6.128 +    $count = $db->selectall_arrayref("SELECT COUNT(*) FROM commands");
   6.129 +    my $offset=$$count[0][0]-$last;
   6.130 +    return $db->selectall_arrayref("SELECT $Fields FROM commands LIMIT $last OFFSET $offset");
   6.131 +}
   6.132 +
   6.133 +sub select_interval($) {
   6.134 +    my $interval = shift;
   6.135 +    my $grep = shift;
   6.136 +
   6.137 +    my $q;  # Рабочая переменная для запросов
   6.138 +# %
   6.139 +    $interval='1,$' if ($interval eq "%");
   6.140 +#        return $db->selectall_arrayref(
   6.141 +#                "SELECT $Fields FROM commands WHERE l3cd='$Context'");
   6.142 +
   6.143 +# Вычисляем номера начальной и конечной строки
   6.144 +# на основе интервала
   6.145 +    my ($start,$stop);
   6.146 +    my ($start_n, $stop_n, $curr_n) = ("","",""); # номера начальной и конечной строк
   6.147 +    if ($interval =~ /,/) {
   6.148 +        ($start,$stop) = split(/,/,$interval);
   6.149 +    }
   6.150 +    else {
   6.151 +        $start = $interval;
   6.152 +        $stop = $interval;
   6.153 +    }
   6.154 +
   6.155 +    $q = $db->selectall_arrayref("SELECT COUNT(*) FROM commands");
   6.156 +    my $count = $$q[0][0];
   6.157 +
   6.158 +# Номер текущей строки
   6.159 +# По умолчанию текущая строка указывает на последнюю
   6.160 +    $curr_n = $count;
   6.161 +    #$curr_n = 2;
   6.162 +
   6.163 +    if ($start =~ /^[0-9]*$/) { $start_n = $start; }
   6.164 +    if ($stop =~ /^[0-9]*$/)  { $stop_n  = $stop; }
   6.165 +    if ($start =~ /^-[0-9]*$/) { $start_n = $curr_n + $start; }
   6.166 +    if ($stop =~ /^-[0-9]*$/)  { $stop_n  = $curr_n + $stop; }
   6.167 +    if ($start =~ /^\+[0-9]*$/) { $start_n = $curr_n + $start; }
   6.168 +    if ($stop =~ /^\+[0-9]*$/)  { $stop_n  = $curr_n + $stop; }
   6.169 +    if ($start eq '.') { $start_n = $curr_n; }
   6.170 +    if ($stop eq '.')  { $stop_n  = $curr_n; }
   6.171 +    if ($start eq '$') { $start_n = $count; }
   6.172 +    if ($stop eq '$')  { $stop_n  = $count; }
   6.173 +    if ($start =~ m@^/(.*)/@ ) {
   6.174 +        $q = $db->selectall_arrayref(
   6.175 +                "SELECT id FROM commands WHERE cline REGEXP '$1' LIMIT 1");
   6.176 +        $start_n=$$q[0][0];
   6.177 +    }
   6.178 +    if ($stop =~ m@^/(.*)/@ ) {
   6.179 +        $q = $db->selectall_arrayref(
   6.180 +                "SELECT id FROM commands 
   6.181 +                 WHERE cline REGEXP '$1' 
   6.182 +                        AND (id >= $start_n) LIMIT 1");
   6.183 +        $stop_n=$$q[0][0];
   6.184 +    }
   6.185 +    if ($start =~ m@^o/(.*)/@ ) {
   6.186 +        $q = $db->selectall_arrayref(
   6.187 +                "SELECT id FROM commands WHERE output REGEXP '$1' LIMIT 1");
   6.188 +        $start_n=$$q[0][0];
   6.189 +    }
   6.190 +    if ($stop =~ m@^o/(.*)/@ && $start_n) {
   6.191 +        $q = $db->selectall_arrayref(
   6.192 +                "SELECT id FROM commands 
   6.193 +                 WHERE output REGEXP '$1' AND (id >= $start_n) LIMIT 1");
   6.194 +        $stop_n=$$q[0][0];
   6.195 +    }
   6.196 +
   6.197 +    if (not $start_n or not $stop_n) {return ""};
   6.198 +
   6.199 +    $stop_n = $count if $count < $stop_n;
   6.200 +    $start_n =1 if 1 > $start_n;
   6.201 +
   6.202 +    my $offset=$start_n-1;
   6.203 +    my $limit = $stop_n - $offset;
   6.204 +
   6.205 +    my $pre_follow=$Follow;
   6.206 +    $q = $db->selectall_arrayref(
   6.207 +            "SELECT id FROM commands 
   6.208 +            WHERE ((id>=$start_n AND id<=$stop_n AND id>$pre_follow) $grep)
   6.209 +            ORDER BY id DESC");
   6.210 +    $Follow = $$q[0][0] if $$q[0][0];
   6.211 +    return $db->selectall_arrayref(
   6.212 +            "SELECT $Fields FROM commands 
   6.213 +            WHERE ((id>=$start_n AND id<=$stop_n AND id>$pre_follow) $grep)");
   6.214 +}
   6.215 +
   6.216 +sub grep_options($$) {
   6.217 +=cut
   6.218 +    -c ищем только в комадной строке    # по умолчанию
   6.219 +    -o ищем только в выводе
   6.220 +    -f ищем и там, и там
   6.221 +    -v инверсия
   6.222 +=cut
   6.223 +    my $regexp=shift; return "" if $regexp eq '';
   6.224 +    my $options=shift;
   6.225 +    my @options = split //, $options;
   6.226 +    my $not = "";
   6.227 +    $not = "NOT" if grep(/v/,@options);
   6.228 +
   6.229 +    my $grep ="";
   6.230 +    if (grep(/f/,@options)) {
   6.231 +        $grep = "AND $not ((cline REGEXP '$regexp') OR (output REGEXP '$regexp'))";
   6.232 +    }
   6.233 +    elsif (grep(/o/,@options)) {
   6.234 +        $grep = "AND $not (output REGEXP '$regexp')";
   6.235 +    }
   6.236 +    elsif (1 || grep(/c/,@options)) {
   6.237 +        $grep = "AND $not (cline REGEXP '$regexp')";
   6.238 +    }
   6.239 +
   6.240 +    return $grep;
   6.241 +}
   6.242 +
   6.243 +sub select_by_cline($) {
   6.244 +    my $cline = $_[0];
   6.245 +    return $db->selectall_arrayref("SELECT $Fields FROM commands WHERE cline GLOB '$cline'");
   6.246 +}
   6.247 +
   6.248 +sub print_commands($)
   6.249 +{
   6.250 +    my @fields=@Fields;
   6.251 +    my $fields=join(", ",@fields);
   6.252 +    my $all=$_[0];
   6.253 +    foreach my $row (@$all) {
   6.254 +        my $cl;       #Каждая строка
   6.255 +        my $i=0;
   6.256 +        for my $f (@fields) {
   6.257 +            $cl->{$fields[$i]}=$$row[$i];
   6.258 +            $i++;
   6.259 +        }
   6.260 +        print $cl->{id}." ".strdequot($cl->{cline})."\n";
   6.261 +    }
   6.262 +}
   6.263 +sub print_all($) {
   6.264 +    my @fields=@Fields;
   6.265 +    my $fields=join(", ",@fields);
   6.266 +    my $all=$_[0];
   6.267 +    my $prev_cl; # Предыдущая строка
   6.268 +    my $tab_seq; # Номер в табуляционной последовательности
   6.269 +    foreach my $row (@$all) {
   6.270 +        my $cl;       #Каждая строка
   6.271 +        my @anchor;   #Начальная строка, якорь, по которому привязывается команда
   6.272 +        my $res="";   #Буфер, в который выводится результат вывода каждой строки
   6.273 +        $i=0;
   6.274 +        for my $f (@fields) {
   6.275 +            $cl->{$fields[$i]}=$$row[$i];
   6.276 +            $i++;
   6.277 +        }
   6.278 +        next    if ($Config{"skip_empty"} =~ /^y/i     && $cl->{"cline"} =~ /^\s*$/ )
   6.279 +                || ($Config{"skip_wrong"} =~ /^y/i     && $cl->{"err"} != 0)
   6.280 +                || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130);
   6.281 +
   6.282 +        # Время
   6.283 +        # Добавляем спереди 0 для удобочитаемости
   6.284 +        my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time});
   6.285 +        $min  = "0".$min  if $min  =~ /^.$/;
   6.286 +        $hour = "0".$hour if $hour =~ /^.$/;
   6.287 +        $sec  = "0".$sec  if $sec  =~ /^.$/;
   6.288 +
   6.289 +        if ($Config{"show_time"} =~ /^y/i) {
   6.290 +            push @anchor,"$hour:$min:$sec ";
   6.291 +        }
   6.292 +
   6.293 +        if ($cl->{"err"}) {
   6.294 +            push @anchor, "err=".$cl->{'err'};
   6.295 +        }
   6.296 +        if ($cl->{"tab"}) {
   6.297 +            push @anchor, "tab=".$cl->{'tab'};
   6.298 +        }
   6.299 +        $res.="#=".join("",@anchor)."\n";
   6.300 +        $res.=$cl->{prompt}.strdequot($cl->{cline})."\n";
   6.301 +
   6.302 +        my $output = ""; # фомируем вывод команды
   6.303 +        my $last_command = $cl->{"last_command"};
   6.304 +        if (!( $Config{"suppress_editors"} =~ /^y/i 
   6.305 +               && grep ($_ eq $last_command, @{$Config{"editors"}}) 
   6.306 +            || $Config{"suppress_pagers"}  =~ /^y/i 
   6.307 +               && grep ($_ eq $last_command, @{$Config{"pagers"}})
   6.308 +            || $Config{"suppress_terminal"}=~ /^y/i 
   6.309 +               && grep ($_ eq $last_command, @{$Config{"terminal"}})
   6.310 +            )) {
   6.311 +            $output = strdequot($cl->{output});
   6.312 +
   6.313 +            # Вырезаем слишком длинные выводы
   6.314 +            my @lines = split '\n', $output;
   6.315 +            if (!$Config{"command_id"} 
   6.316 +                && ( $Config{"head_lines"} || $Config{"tail_lines"})
   6.317 +                && $#lines >  $Config{"head_lines"} + $Config{"tail_lines"}) {
   6.318 +                    $output="";
   6.319 +                    for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) {
   6.320 +                        $output .= $lines[$i]."\n";
   6.321 +                    }
   6.322 +                    $output .= $Config{"skip_text"}."\n";
   6.323 +
   6.324 +                    my $start_line=$#lines-$Config{"tail_lines"}+1;
   6.325 +                    for (my $i=$start_line; $i<= $#lines; $i++) {
   6.326 +                        $output .= $lines[$i]."\n";
   6.327 +                    }
   6.328 +                } 
   6.329 +            }
   6.330 +        $res.=$output;
   6.331 +        if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) {
   6.332 +            $res.= $cl->{"diff"}
   6.333 +        }
   6.334 +        #open(OUTPUT, '|sendxmpp nata_samoylenko@jabber.ru');
   6.335 +        #print OUTPUT $res;
   6.336 +        #close(OUTPUT);
   6.337 +        print $res;
   6.338 +        $res="";
   6.339 +        $prev_cl=$cl;
   6.340 +    }
   6.341 +}
   6.342 +
   6.343 +sub strdequot($)
   6.344 +{
   6.345 +    my $text = join "", @_;
   6.346 +    $text =~ s/&amp;/&/g;
   6.347 +    $text =~ s/&lt;/</g;
   6.348 +    $text =~ s/&gt;/>/g;
   6.349 +    $text =~ s/&quot;/"/g;
   6.350 +    return $text;
   6.351 +}
   6.352 +
   6.353 +sub show_usage()
   6.354 +{
   6.355 +    print <<EOF;
   6.356 +Usage:
   6.357 +    l3 [INTERVAL] [grep [KEYS] REGEXP] [filter [KEYS] FILTER] [COMMAND [KEYS]]
   6.358 +
   6.359 +Commands:
   6.360 +    history
   6.361 +    tail
   6.362 +    head
   6.363 +    cat (default)
   6.364 +
   6.365 +    If COMMAND is not specified, cat assumed.
   6.366 +
   6.367 +EOF
   6.368 +}