niedziela, 23 sierpnia 2015

Oswajanie Salamandry

W poprzednim artykule napisałem, że programowi Servant Salamander brakuje możliwości automatycznego otwierania właściwego edytora lub przeglądarki w zależności od typu wskazanego pliku. Można wprawdzie podłączyć zewnętrzny edytor i przeglądarkę, ale w ten sposób da się zazwyczaj uzyskać obsługę tylko jednego rodzaju plików, najczęściej tekstowych.

Rozwiązanie narzuca się samo: a gdyby tak podłączyć jakiś uniwersalny edytor i przeglądarkę, zdolne rozpoznawać typ pliku i odpowiednio dostosowywać swoje działanie? Problem w tym, że takich programów nie ma, a jeśli już, to obsługują pliki określonego rodzaju, np. graficzne. To nie zapewnia wymaganej uniwersalności.

Spróbujmy zatem rozważyć, co można zrobić w takim przypadku.
  1. Napisać samemu odpowiednie programy: uniwersalny edytor plików i przeglądarkę. To rozwiązanie odrzuciłem „w przedbiegach”. Przy moich umiejętnościach programistycznych tworzenie takich aplikacji musiałoby trwać latami, wyszłyby z tego straszne „kobyły” (tak nazywam programy o monstrualnych rozmiarach, przeładowane funkcjami i zwykle bardzo wolno działające) i wreszcie – praca nie miałaby końca, gdyż ciągle pojawiają się nowe formaty plików warte obsłużenia.
  2. Napisać program, który rozpoznawałby rozszerzenie nazwy pliku i na tej podstawie uruchamiał odpowiedni edytor lub przeglądarkę. To już lepiej, ale wywoływanie programu z poziomu innego programu może generować problemy z zarządzaniem pamięcią i kompatybilnością z różnymi wersjami systemu Windows. Nie bardzo miałem ochotę na studiowanie tych zagadnień.
  3. A gdyby tak uruchamianie programów pozostawić systemowi operacyjnemu? W końcu jest to jedna z jego podstawowych funkcji. Wszystkie wersje Windows obsługują skrypty (batch files) – pliki tekstowe zawierające polecenia systemu, z poziomu których bez problemu można uruchamiać programy. Jest tylko jeden, tyci tyci problem: w systemie nie ma polecenia rozpoznawania rozszerzenia nazwy pliku.
Postanowiłem wykorzystać sposób trzeci, a brakujące polecenie dołożyć do systemu operacyjnego przy pomocy zewnętrznego programu. Napisanie czegoś takiego wydawało mi się o wiele prostsze niż stworzenie aplikacji wywołującej inne programy, nie mówiąc już o uniwersalnej przeglądarce.

Tylko jak miałoby wyglądać użycie takiego programu na poziomie skryptu? Język skryptowy systemu Windows obsługuje zmienne środowiskowe – wartości które można modyfikować i na ich podstawie tworzyć konstrukcje logiczne typu: jeśli wartość zmiennej taka, to zrób to, a jeśli inna – to co innego. Wydawałoby się zatem nic prostszego, gdyby program ustawiał wartość jakiejś zmiennej środowiskowej, nazwijmy ją extension, na ciąg znaków stanowiący rozszerzenie nazwy pliku (czyli np. DOC, XLS, PDF, GIF itp.). Niestety, znowu pojawia się problem: każdy program uruchomiony w systemie Windows tworzy sobie własną kopię zestawu zmiennych środowiskowych, która jest usuwana z pamięci po zakończeniu jego działania. Gdybym zatem w programie ustawił wartość zmiennej extension, to po jego zamknięciu wartość ta byłaby niedostępna dla skryptu, działającego na własnej kopii „środowiska”.

Zaawansowani programiści znają rozwiązanie tego problemu. Istnieją metody wyszukiwania w pamięci „nadrzędnego” zestawu zmiennych środowiskowych należącego do procesu wywołującego program (w naszym przypadku byłby to systemowy interpreter poleceń wykonujący skrypt) i modyfikowania go. Ja jednak nie jestem zaawansowanym programistą i nie posiadam niezbędnej wiedzy, której zdobycie mogłoby mi zająć dość dużo czasu. Obawiałem się ponadto, że tego rodzaju „sztuczki” mogą wymagać modyfikacji w zależności od wersji systemu operacyjnego i zastosowanego języka programowania. Znacznie szybsze, a przy tym uniwersalne, okazało się użycie „protezy”.

Mój program extension.exe (napisany w Pascalu i skompilowany Free Pascalem) działa następująco: pobiera nazwę pliku zadaną jako parametr wywoływania, wycina rozszerzenie (ciąg znaków występujących po ostatniej kropce) i generuje na wyjściu polecenie systemowe set ustawiające wartość zmiennej extension. Najłatwiej zobaczyć to na przykładzie.

Efektem wywołania:
extension Alamakota.txt

będzie wyświetlenie tekstu:
set extension=TXT

Plik extension.exe po pobraniu należy zapisać gdzieś na dysku twardym, ja dla porządku utworzyłem sobie na tego rodzaju narzędzia katalog C:\Util.

Pora teraz utworzyć skrypt otwierający właściwy edytor dla konkretnego pliku. U mnie nazywa się on SalamEd.bat (bo Edytor dla „Salamandry”) i jest zapisany w katalogu C:\Bat.

Oto jego zawartość:

@echo off
c:\util\extension %1 >c:\bat\setext.bat
call c:\bat\setext.bat

if "%extension%" == "ABW" goto abiword
if "%extension%" == "GIF" goto gimp
if "%extension%" == "JPG" goto gimp
if "%extension%" == "HTM" goto html
if "%extension%" == "HTML" goto html
if "%extension%" == "ODT" goto swriter
goto default

:abiword
q:\portableapps\abiwordportable\abiwordportable.exe %1
goto end

:gimp
Q:\PortableApps\GIMPPortable\GIMPPortable.exe %1
goto end

:html
d:\winprog\nvu\nvu.exe %1
goto end

:swriter
"C:\Program Files\OpenOffice.org 3\program\swriter.exe" %1
goto end

:default
start c:\windows\notepad.exe %1
rem c:\bate\ditor.bat %1
goto end

:end


Pierwszy wiersz skryptu ma charakter jedynie kosmetyczny: wyłącza wyświetlanie dalszych poleceń podczas wykonywania. Samo polecenie „@echo off” też się nie wyświetla, dzięki znakowi „@” na początku.

Drugi wiersz jest bardzo ważny: wywołujemy program extension podając jako parametr zawartość zmiennej %1, która zawiera nazwę pliku zadaną jako parametr wywołania całego skryptu (tym się będzie zajmował Servant Salamander). Efekt działania programu, który normalnie wyświetliłby się na ekranie, przekierowujemy (znak „>”) do skryptu setext.bat, który zostanie utworzony (lub nadpisany, jeśli istnieje) w katalogu C:bat. Plik setext.bat zawiera zatem polecenie ustawiające zmienną extension na wartość rozszerzenia nazwy pliku przeznaczonego do edycji.

W trzecim wierszu, z poziomu skryptu SalamEd.bat uruchamiamy skrypt setext.bat (tak, można to robić), który ustawi nam wartość zmiennej extension. Jako że zagnieżdżone skrypty wykonywane są przez ten sam program (interpreter poleceń), działają na tej samej kopii środowiska, zatem wartość zmiennej extension ustawiona przez skrypt setext.bat będzie dostępna na poziomie skryptu SalamEd.bat. O to właśnie chodziło!

Dalej następuje sekwencja instrukcji „if” i „goto”, których działanie można opisać następująco: sprawdź, jakie jest rozszerzenie nazwy pliku i na tej podstawie skocz do sekcji skryptu, uruchamiającej właściwy edytor.

Ważne, żeby na końcu tej sekwencji znalazła się instrukcja „goto default”. Otóż założyłem sobie, że jeśli znajdzie się plik o rozszerzeniu niezdefiniowanym w skrypcie, to ma być podjęta próba otwarcia go w edytorze tekstowym (w tym przypadku – notatniku). „Załatwia” to całą masę logów, skryptów, źródeł programów i innych plików o różnych dziwnych rozszerzeniach, a zawierających czytelny tekst, zaś w przypadku plików nietekstowych często pozwala się zorientować, z czym mamy do czynienia.

Następnie następują sekcje wywołujące odpowiedni program do edycji w rodzaju:

:swriter
"C:\Program Files\OpenOffice.org 3\program\swriter.exe" %1
goto end

Każda taka sekcja zaczyna się etykietą rozpoczynającą się znakiem „:”, która nie jest poleceniem, tylko adresem dla instrukcji skoku (porównaj z instrukcjami „goto”, gdzie nazwa etykiety występuje bez dwukropka).

Dalej jest wywołanie odpowiedniego programu. Musimy wprowadzić pełną ścieżkę dostępu do pliku wykonywalnego, uprzednio sprawdziwszy, gdzie program jest zainstalowany w naszym systemie. Jeśli w ścieżce występują spacje, to musi ona być ujęta w cudzysłowy. Zmienna „%1” zawiera oczywiście nazwę naszego pliku do edycji.

Warto zwrócić uwagę na instrukcję „goto end” w każdej sekcji. Gdyby jej nie było, to plik byłby otwierany po kolei we wszystkich edytorach wywoływanych dalej w skrypcie, a nie o to nam chodzi.

Proszę jeszcze o zwrócenie szczególnej uwagi na dwie ostatnie sekcje. Pierwsza, rozpoczynająca się etykietą „:default” służy do określenia programu (w naszym przykładzie – notatnik) wywoływanego w przypadku, gdy skrypt nie rozpozna rozszerzenia pliku. Druga zawiera wyłącznie etykietę „:end”. Skok do tej etykiety powoduje zakończenie działania skryptu.

Mam nadzieję, że teraz już każdy będzie w stanie napisać analogiczny skrypt do swoich potrzeb, a także rozbudowywać go o obsługę innych typów plików. Oczywiście skopiowanie mojego przykładu na nic się nie zda. Instrukcje warunkowe muszą być dostosowane do tego jakie pliki obsługujemy, zaś wywołania programów – do tego co i gdzie mamy zainstalowane na komputerze.

Analogiczny skrypt należy utworzyć do wywoływania przeglądarek. U mnie jest to C:\Bat\SalamView.bat i jego treść zamieszczam jako przykład, już bez komentarza. Przeanalizujcie sobie sami.

@echo off
c:\util\extension %1 >c:\bat\setext.bat
call c:\bat\setext.bat

if "%extension%" == "3GP" goto video
if "%extension%" == "DBF" goto dbf
if "%extension%" == "DOC" goto wordview
if "%extension%" == "RTF" goto wordview
if "%extension%" == "TXT" goto wordview
if "%extension%" == "HTM" goto wordview
if "%extension%" == "HTML" goto wordview
if "%extension%" == "JPG" goto iview
if "%extension%" == "XLS" goto excelview
if "%extension%" == "PDF" goto sumatra
goto default

:dbf
start c:\util\dbv.exe %1
goto end

:excelview
"c:\program files\microsoft office\office12\xlview.exe" %1
goto end

:html
d:\winprog\firefox\firefox.exe %1
goto end

:iview
q:\winprog\Iview425\i_view32.exe %1
goto end

:wordview
rem "c:\program files\microsoft office\office11\wordview.exe" %1
Q:\PortableApps\AlReader2.win32.en\AlReader2.exe %1
goto end

:video
"c:\Program Files\Media_Player_Classic\MPLAYERC.EXE" "%1"
goto end

:sumatra
"c:\Program Files\SumatraPDF\SumatraPDF.EXE" %1
goto end

:default
start c:\util\hiew.exe %1
goto end

:end


Teraz należy „podpiąć” wykonane skrypty do „Salamandry”. Klikamy „Options” → „Configuration” i na zakładce „Viewer/Editor” wypełniamy pola jak na ilustracji.


Należy uważać, żeby nie była zaznaczona opcja „Use long filenames for arguments” gdyż w przeciwnym razie nazwy plików zawierające spacje będą nieprawidłowo przekazywane do skryptu (spacja jest w tym przypadku separatorem parametrów).

Dygresja

Spacje w nazwach plików i katalogów prędzej czy później stają się źródłem problemów. Do swojego użytku przyjąłem następującą konwencję: spacje zastępuję znakami podkreślenia, a „polske” litery – znakami podstawowego alfabetu („a” zamiast „ą” itp.). Czytelności to nie zmniejsza, a bardzo ułatwia życie. Polecam.


Pamiętajmy, żeby jeszcze wykonać „Options” → „Save Configuration”. Nawet jeśli mamy aktywne automatyczne zapisywanie konfiguracji programu (tak jest domyślnie), nie zaszkodzi to zrobić „na wszelki wypadek”.

Pora na test: zaznaczmy w oknie Servant Salamandera jakiś plik graficzny i naciśnijmy klawisz F4. Powinien otworzyć się edytor graficzny (u mnie będzie to GIMP) z załadowanym plikiem gotowym do edycji. Po naciśnięciu F3 plik powinien otworzyć się w wybranej przeglądarce (u mnie – IrfanView). Sprawdźmy te akcje dla wszystkich typów plików zdefiniowanych w skryptach, a następnie przetestujmy otwieranie się domyślnego edytora i przeglądarki dla plików niezdefiniowanych. Jeśli skrypty napisane są poprawnie, wszystko będzie działało bez problemów, jeśli nie – błędy będą łatwe do wychwycenia i skorygowania.

Warto zaznaczyć, że do danego typu pliku mamy przypisane nie dwa, lecz trzy programy, po naciśnięciu klawisza Enter zadziała bowiem standardowe przyporządkowanie systemu Windows.

No i co: czy teraz jeszcze ktoś potrzebuje koniecznie Total Commandera?

Windows XP
Opisane powyżej „sztuczki” działają we wszystkich wersjach systemu Windows, od 95 do XP. Ten ostatni ma jednak swoją specyfikę objawiającą się tym, że po zamknięciu niektórych programów otwieranych z poziomu skryptu, pozostają otwarte okna interpretera poleceń. Jeśli to komuś przeszkadza, proponuję pobrać program cmdow.exe, zapisać go w katalogu C:\Util, a następnie w skryptach SalamEd.bat i SalamView.bat jako drugi wiersz umieścić wpis:

c:\util\cmdow @ /hid
Nie jest to rozwiązanie problemu, tylko jego zamaskowanie, w praktyce jednak sprawdza się znakomicie.

Brak komentarzy:

Prześlij komentarz