2008.06_MiniCommander – własne dwa panele_[Programowanie].pdf

(149 KB) Pobierz
439032723 UNPDF
Programowanie
Arkusz kalkulacyjny i makra
własne dwa panele
Marek Sawerwain
Zarządzanie plikami to jedna z najważniejszych i zarazem najczęściej wykonywana czynność podczas
korzystania z systemu operacyjnego, czyli mówiąc wprost podczas korzystania z komputera. Dla
systemu Linux, powstało już wiele różnych wersji tego typu programów. Niewątpliwie najbardziej
popularny jest program o nazwie Midnight Commander. Wzorcem dla tego programu był Norton
Commander, który w czasach systemu DOS znacząco poprawiał komfort korzystania z systemu.
nie był jedynym programem do zarządza-
nia plików. Drugim tego typu programem
o nieco innej fi lozofi i pracy był program
XTree. Podobnie jak Norton Commander również posiadał za-
gorzałych fanów. Kolejnym programem do zarządzania, który
także cieszył się ogromną popularnością był Directory Opus.
Jednakże był on dostępny na komputery Amiga. Użytkownicy
Linuksa są w bardzo komfortowej sytuacji, ponieważ powstało
kilkanaście różnych programów do zarządzania plikami. Istnie-
ją odpowiedniki Norton Commandera, jak wspomniany Midni-
ght Commander oraz równie dobry - jeśli nie lepszy - Krusader.
Wersji programu naśladującego XTree jest kilkanaście, powstał
nawet odpowiednik Directory Opusa - nazywa się Worker.
Natomiast naszym zadaniem będzie napisanie programu
podobnego do Midnight Commandera. Skupimy się tylko na
najważniejszych opcjach. Jednak będziemy mieli na uwadze
fakt, aby nasz program był użyteczny, czyli potrafi ł kopiować
pliki oraz tworzyć i kasować katalogi. Niestety nie jest możli-
we nawet pobieżne przedstawienie wszystkich elementów ko-
du źródłowego - jest on zbyt duży. Dlatego zostaną przedsta-
wione tylko podstawowe elementy.
Podstawowe informacje
W naszym programie będą dostępne tylko podstawowe opcje,
jednak mimo tych ograniczeń kod źródłowy liczy sobie 44kb.
To naturalnie nadal niewielki program, ale koniecznie trzeba
przygotować diagram najważniejszych zdarzeń. Rysunek 1
przedstawia taki schemat.
Pierwsze zdarzenie jakie pojawia się po uruchomieniu
programu dotyczy obsługi trybu tekstowego. Musimy od-
powiednio uruchomić obsługę konsoli. Następnie nasz pro-
gram wchodzi w główną pętlę. W pętli sprawdzamy, które
klawisze zostały użyte przez użytkownika. Jeśli używane są
kursory, to na aktywnym panelu odpowiednio przesuwamy
kursor wskazujący na aktualny katalog bądź plik. Wciśnię-
cie klawisza TAB powoduje zmianę panelu ma przeciwny.
Obsługujemy także inne klawisze, jak np.: F7 , czyli two-
rzenie katalogu. Po naciśnięciu tego klawisza wyświetlamy
odpowiednie okno dialogowe. Obsługujemy także kombi-
nacje klawiszy, jak np.: CTRL-A - wciśnięcie tych klawiszy
powoduje pojawienie się okna do zmiany atrybutów pliku
lub katalogu.
Schemat z Rysunku 1 nie przedstawia wszystkich klawi-
szy, jednakże pozostałe klawisze są obsługiwane w podobny
56
czerwiec 2008
MiniCommander –
W arto przypomnieć, iż Norton Commander
439032723.002.png
Programowanie
Arkusz kalkulacyjny i makra
sposób. Na koniec naturalnie trzeba zamknąć
tryb tekstowy.
Kod źródłowy naszego programu został
podzielony na kilka części. Pliki fList.c oraz
fList.h to implementacja dynamicznej listy
dwukierunkowej, która pomaga w realizacji li-
sty plików i katalogów wyświetlanych w pane-
lu. Obsługa okien dialogowych została umiesz-
czona w plikach dialogs.c oraz dialogs.h . Re-
alizacja operacji na plikach i katalogach, np.:
usuwanie katalogu, to zadanie dla funkcji znaj-
dujących się plikach fs_oper.c oraz fs_op er.h .
Obsługa paneli, czyli ich rysowanie oraz ob-
sługa klawiatury została umieszczona w pli-
kach workscr.c i workscr.h . Ostatnim plikiem
jest maincode.c . W tym pliku znajduje się funk-
cja main , w której wykonujemy wszystkie nie-
zbędne czynności, aby uruchomić nasz pro-
gram, czyli uruchamiamy tryb tekstowy, a po
wciśnięciu klawisza F10 , kończącego pracę na-
szego programu, w funkcji main wykonywane
są ostatnie czynności związane z zamknięciem
trybu tekstowego.
do katalogu. Naturalnie tylko wtedy, kiedy
mamy do tego prawo. Tę sytuację łatwo wy-
kryć sprawdzając choćby, czy wartością funk-
cji opendir jest wartość NULL. Jeśli tak jest,
może to oznaczać, iż nie mamy prawa dostę-
pu do katalogu. Gdy operacja się udała, za po-
mocą wywołań readdir będziemy odczyty-
wać zawartość katalogu, element po elemen-
cie. Poszczególne elementy są typu struct
dirent . Struktura ta opisuje jeden wpis w ka-
talogu. Zapisem do listy zajmuje się funkcja
DecomposeDirent , która wydziela ze struktu-
ry dirent poszczególne elementy, jak nazwę i
rodzaj wpisu: plik, katalog, link symbolicz-
ny. Odczytywane są także prawa dostępu oraz
czas modyfi kacji. Te elementy są wyświetla-
ne w panelu. Proces odczytywania zakończy
się w momencie, gdy funkcja readdir zwróci
wartość NULL.
Odczytana zawartość katalogu niestety nie
jest posortowana - jej kolejność jest zazwyczaj
określona przez datę utworzenia. My jednak
będziemy sortować alfabetycznie: posortowa-
niem odczytanej zawartości zajmuje się funk-
cja SortList .
Listing 2. zawiera pełny kod źródłowy funk-
cji kopiującej plik. Funkcja ta posiada cztery
argumenty. Pierwsze dwa to ścieżka źródło-
wa oraz ścieżka docelowa. Trzeci argument to
wielkość kopiowanego pliku. W ostatnim ar-
gumencie podajemy prawa pliku.
Sam proces kopiowania polega na przepi-
sywaniu zawartości pliku małymi fragmenta-
mi. Jeden fragment to około 64kb. Dlatego na-
leży obliczyć ile bloków danych, czyli małych
fragmentów, trzeba przegrać. Naturalnie trze-
ba obliczyć resztę z dzielenia (ponieważ rzad-
ko bywa aby plik miał idealną długość po-
dzielną przez pewną wielkość):
blocks=len / 65000
remaing=len % 65000;
Po tych obliczeniach w pętli kopiujemy blo-
ki danych:
for(i=0;i<blocks;i++) {
le=read(f1, buf, 65000);
le=write(f2, buf, 65000);
}
Odczytanie zawartości katalogu
Zanim poszczególne nazwy zostaną wyświe-
tlone w panelu, należy odczytać zawartość ka-
talogu. Funkcja, która wykonuje to zadanie
jest dość krótka. Przedstawiona została na Li-
stingu 1. Odczyt zawartości wskazanego ka-
talogu jest dość łatwy do zrealizowania. Wy-
wołując funkcję opendir uzyskujemy dostęp
Kopiowanie pliku
Tworzony przez nas program potrafi kopiować
pojedyncze pliki oraz całe katalogi. Najważ-
niejszą funkcją jest funkcja kopiująca poje-
dynczy plik, ponieważ funkcja kopiowania re-
kurencyjnego korzysta z opisywanej funkcji.
Kopiowanie ostatniego fragmentu danych jest
podobne, wykorzystujemy zmienną remaing ,
w której mamy informacje ile danych trzeba
przegrać, aby plik był kompletny.
le=read(f1, buf, remaing);
le=write(f2, buf, remaing);
START
Po przegraniu danych ustalamy jeszcze pra-
wa za pomocą polecenia fchmod . Zwróćmy
też uwagę (potwierdza to Listing 2.), iż jeśli
plik jest mniejszy niż wielkość jednego bloku
danych, to kopiujemy jego zawartość jednym
wywołaniem funkcji read oraz write .
inicjalizacja
trybu tekstowego
odczyt aktualnego
katalogu
TAK
Czy naciśnięto
klawisz TAB?
zmiana panelu
NIE
obsługa klawiatury
Biblioteka S-Lang
Tworzenie
katalogu?
TAK
procedura
tworzenia katalogu
Biblioteka S-Lang oferuje funkcje do obsługi
konsoli, czyli ekranu oraz klawiatury. Funk-
cje te pracują zarówno na niskim i wysokim
poziomie, co oznacza, że jeśli stosujemy
funkcję wysokiego poziomu nie musimy się
martwić o detale związane z obsługą konso-
li. Mamy także dostęp do prostego edytora
liniowego (Readline), co znacząco ułatwia
odbiór danych od użytkownika. Zaletą pa-
kietu S-Lang jest również wydajność. Pakiet
ten oferuje również dostęp do interpretowa-
nego języka programowania podobnego do
C. W naszym programie jednak nie korzy-
stamy z tej możliwości.
Edycja
atrubutów
TAK
procedura
edycji atrybutów
NIE
...
NIE
koniec pracy
programu?
TAK
zamknięcie
trybu tekstowego
STOP
Rysunek 1. Schemat przepływu sterowania w projektowanej aplikacji
www.lpmagazine.org
57
439032723.003.png
Programowanie
Arkusz kalkulacyjny i makra
Listing 1. Odczytanie zawartości katalogu
Lista plików i katalogów w panelu Listing
3. zawiera fragment z funkcji DisplayDirec-
tory , która jest odpowiedzialna na wyświetla-
nie listy plików i katalogów w lewym panelu
(wersja dla prawego jest naturalnie podobna).
Stosujemy pętlę for, która wykorzystuje zmien-
ne ifrom_dir_left oraz ito_dir_left . W
zmiennych tych zapisane są numery, od któ-
rego wpisu w katalogu należy rozpocząć wy-
świetlanie i na którym należy zakończyć. Ilość
elementów jest wyliczana przez funkcję Cal-
culate . Funkcja ta, uwzględniając wielkość, a
dokładniej wysokość konsoli, oblicza wielkość
okna podglądu. Zmienne ifrom_dir_left
oraz ito_dir_left są również odpowiednio
modyfi kowane. Gdy użytkownik naciska kla-
wisze strzałek, wtedy przesuwany jest kursor,
czyli zmienna cpos_left. Zmienne te są też
modyfi kowane, gdy naciskane są klawisze Pa-
ge Up oraz Page Down . Obsługa klawiszy znaj-
duje się w funkcji NavigateKey .
Wyświetlanie informacji o plikach (zebra-
nych przez funkcję ReadDirectory ) rozpoczy-
namy od odczytania odpowiedniego elementu z
listy funkcją At0List . Następnie w zależności
int ReadDirectory ( char * path ) {
DIR * dir = NULL ;
struct dirent * idir = NULL ;
TDirItem tmp ;
dir = opendir ( path );
if ( dir == NULL ) {
return - 1 ;
}
while ( idir = readdir ( dir )) {
DecomposeDirent ( idir );
}
closedir ( dir );
SortList ( active_dir , IDirCmp ,
IDirAssign , & tmp );
return 0;
}
Listing 4. Odczytanie jednej linii tekstu, wykorzy-
stując edytor Readline
char * ReadLineText ( int x , int y ,
int w , char * txt ) {
int n ;
unsigned char * line ;
rline_BUF [ 0 ]= 0 ;
init_slang_readline ( x , y , w );
if ( txt != NULL ) {
SLrline_set_line ( rline_RLI ,
txt );
SLrline_set_point ( rline_RLI ,
strlen ( txt ));
}
line = SLrline_read_line ( rline_
RLI , NULL , & len );
strcpy ( & rline_BUF [ 0 ] , line );
SLfree ( line );
SLrline_close ( rline_RLI );
return & rline_BUF [ 0 ];
}
Listing 3. Funkcja kopiująca plik
case PANEL_LEFT :
for ( i = ifrom_dir_left ; i <= ito_
dir_left ; i ++) {
di =( TDirItem *) At0List ( dir_
left , i );
switch ( di -> ft ) {
case ft_DIR :
sprintf ( t , DISP_DIR , di -> name , di -
> mtime ); break ;
case ft_LNK :
sprintf ( t , DISP_LNK , di -> name , di -
> mtime ); break ;
case ft_REG :
sprintf ( t , DISP_REG , di -> name ,
IntToMB ( di -> size ) , di -> mtime );
break ;
default : sprintf ( t ,
"<unk>" );
}
if ( i == cpos_left ) {
Slsmg_set_color ( 3 );
SLsmg_gotorc ( rd ++ , 1 );
Slsmg_printf ( "%s" , t );
/* dodatkowe informacje na
dole okna*/
Slsmg_set_color ( 5 );
SLsmg_gotorc ( SLtt_Screen_
Rows - 2 , 2 );
sprintf ( t , ADD_INFO ,
di -> name , ReturnMode ( di -> mode ) ,
IntToMB ( di -> size ));
SLsmg_printf ( "%s" , t );
}
else {
SLsmg_set_color ( 5 );
SLsmg_gotorc ( rd ++ , 1 );
Slsmg_printf ( "%s" , t );
}
}
break ;
Listing 2. Funkcja kopiująca plik
int copy_fi le ( char * from , char * to ,
int len , mode_t mode ) {
int f1 , f2 , i , blocks , remaing ,
le ; void * buf ;
le =- 1 ;
f1 = open ( from , O_RDONLY );
if ( f1 !=- 1 ) {
f2 = open ( to , O_CREAT | O_TRUNC
| O_WRONLY );
if ( f2 ==- 1 ) {
le = close ( f1 );
return - 1 ;
}
buf =( void *) malloc ( 65000 );
if ( len < 65000 ) {
le = read ( f1 , buf , len );
le = write ( f2 , buf , len );
le = fchmod ( f2 , mode );
}
else {
blocks = len / 65000 ;
remaing = len % 65000 ;
for ( i = 0 ; i < blocks ; i ++) {
le = read ( f1 , buf ,
65000 ); le = write ( f2 , buf , 65000 );
}
le = read ( f1 , buf , remaing );
le = write ( f2 , buf , remaing );
le = fchmod ( f2 , mode );
}
free ( buf );
le = close ( f2 ); le = close ( f1 );
}
else {
le =- 1 ;
}
return le ;
}
Listing 5. Okno kasowania pliku
int DeleteDLG ( char * path ) {
int x , y , c , l , le ;
l = strlen ( path )+ 6 + 2 ;
if ( l < 28 ) l = 28 ;
x = SLtt_Screen_Rows / 2 ;
y = SLtt_Screen_Cols / 2 ;
SLsmg_fi ll_region ( x - 2 , y - l / 2 , 4 ,
l + 2 ,' ' );
SLsmg_draw_box ( x - 2 , y - l / 2 , 4 ,
l + 3 );
SLsmg_gotorc ( x - 1 , y - l / 2 + 1 );
SLsmg_printf ( "Delete \" %s \
" " , path );
SLsmg_gotorc ( x , y - 9 );
DrawButton ( "OK" );
SLsmg_gotorc ( x , y );
DrawButton ( "CANCEL" );
Slsmg_refresh ();
if ( Select ( x , y - 9 , y , "OK" ,
"CANCEL" )== 0 ) {
le = recursive_del ( path );
if ( le ==- 1 )
AccessViolationDLG ();
}
}
58
czerwiec 2008
439032723.004.png
Programowanie
Arkusz kalkulacyjny i makra
od typu wpisu (plik, link, katalog) przygotowu-
jemy poleceniem sprintf ciąg znaków.
Kolejny test to sprawdzenie, czy aktualna
wartość zmiennej i jest równa wartości zmien-
nej cpos_left . Jeśli tak jest, to dobierając od-
powiednie zmiany kolorów użytkownik nasze-
go programu będzie widział kursor w panelu.
Inaczej mówiąc, wiadomo będzie na jaki plik
bądź katalog aktualnie wskazujemy. Jeśli bę-
dzie to katalog, to naciśnięcie klawisza ENTER
spowoduje przejście do tego katalogu. Wybranie
dwóch kropek, naturalnie, spowoduje przejście
katalog wyżej.
przez argument w. Natomiast w ostatnim argu-
mencie o nazwie txt możemy podać tekst, któ-
ry zostanie podany edycji. Zmieniony tekst lub
jego kopia, jeśli użytkownik nie zmieni treści,
staje się wynikiem działania funkcji ReadLine-
Text . Funkcja ReadLineText choć dość krótka,
składa się z trzech części. W pierwszej inicjali-
zujemy system Readline dostępny w bibliote-
ce S-Lang . Następnie sprawdzamy zawartość
argumentu txt . Jeśli wartość tej zmiennej jest
różna od NULL, to zawartość argumentu txt
zostanie przepisana do edytora liniowego za
pomocą funkcji Slrline_set_line .
Następny krok to edycja danych. Po wywo-
łaniu funkcji SLrline_read_line użytkownik
może wpisać nowy tekst lub zmienić istniejący.
Proces edycji trwa do momentu wciśnięcia kla-
wisza ENTER . Można też tak oprogramować
obsługę Readline , aby klawiszem TAB można
było opuścić tryb edycji. Po zakończonej edycji
celowo zawartość zmiennej line jest kopiowa-
na do zmiennej globalnej rline_BUF , bowiem
pamięć przydzielona do zmiennej line powinna
zostać zwolniona. Wywołaniem funkcji SLrli-
ne_close zamykamy proces edycji (pomijamy
wywołanie funkcji done_slang_readline ).
znaków, jak również znajdujemy współrzęd-
ne środka ekranu. Następnie za pomocą funkcji
SLsmg_fi ll_region wypełniamy pewien obszar
ekranu jednolitym kolorem, aby następną funk-
cją narysować prostokąt. Wewnątrz tego prosto-
kąta umieszczamy komunikat tekstowy. Rysuje-
my również dwa dodatkowe przyciski - OK oraz
CANCEL . Po tych przygotowaniach możemy
stosując funkcję Select pozwolić użytkowniko-
wi na wybór, czy chce skasować plik wybierając
OK , czy też woli wycofać się z tej operacji po-
przez wybór przycisku CANCEL . Jeśli użytkow-
nik wybierze OK funkcja recursive_del usu-
nie wskazany plik bądź katalog. Jeśli nie mamy
praw dostępu, to w zmiennej le znajdzie się war-
tość -1. Możemy w ten sposób poinformować
użytkownika o braku praw dostępu.
Readline – obieranie
informacji od użytkownika
Podczas tworzenia katalogu, przenoszenia pli-
ku oraz kopiowania, użytkownik ma możliwość
podania nazwy nowego katalogu lub zmianę na-
zwy kopiowanego pliku. Edycja jest możliwa
dzięki zastosowaniu edytora liniowego. Biblio-
teka S-Lang daje możliwość skorzystania z pro-
stego edytora liniowego o nazwie Readline . W
naszym programie występują trzy funkcje: in-
it_slang_readline zajmująca się przygotowa-
niem odpowiednich zmiennych, druga ReadLi-
neText , która zgodnie z nazwą odczytuje linię
tekstu oraz done_slang_readline zamykają-
ca proces odczytu linii tekstu. Wszystkie naj-
ważniejsze czynności są wykonywane w funk-
cji ReadLineText . Jej kod źródłowy prezentu-
je Listing 4. Istotne są argumenty tej funkcji.
Dwa pierwsze, x oraz y , określają współrzędne
na ekranie, w którym będzie znajdować się po-
le edycyjne. Szerokość tego pola jest określona
Podsumowanie
Nie sposób opisać wszystkich detali związa-
nych z naszym programem. Choć ograniczyli-
śmy liczbę możliwości, to jak widać nie jest to
już program do napisania w przysłowiowy jeden
wieczór. Jednakże w tym, bądź co bądź, niewiel-
kim programie udało się zaimplementować pod-
stawowe opcje, takie jak kopiowanie i usuwanie
plików oraz katalogów. Swobodnie możemy się
także poruszać po strukturze katalogowej. Pro-
gram oferuje także możliwość zmiany podsta-
wowych atrybutów plików i katalogów. Wielką
zaletą naszego programu jest też jego wielkość.
Nasz program to zaledwie kilkadziesiąt kilobaj-
tów, choć wymagana jest dodatkowa bibliote-
ka S-Lang . Jednakże, jak zostało to pokazane,
w dość prosty sposób możemy utworzyć wersję
statyczną naszego programu. Jej zaletą jest nie-
zależność od zainstalowanych bibliotek.
W naszym programie brakuje także kilku
ważnych opcji. Wypadałoby zaimplementować
podgląd plików w postaci tekstowej. Oblicza-
nie wielkości zajętego miejsca przez wskazany
katalog także byłoby bardzo przydatną funkcją.
Nieco bardziej skomplikowaną funkcją były-
by dostęp do konsoli.Jednakże ta funkcja spo-
wodowałaby, iż nasz program zyskałby bar-
dzo wiele na funkcjonalności. Wiele większych
oraz mniejszych funkcji można dodać do opisy-
wanego programu, toteż zachęcam do wprowa-
dzania zmian i eksperymentowania z nim.
Okno kasowania pliku bądź katalogu
Listing 5. zawiera całą funkcję odpowie-
dzialną za narysowanie okna dialogowego z za-
pytaniem, czy skasować wskazany przez użyt-
kownika plik bądź katalog. Przed narysowaniem
okna należy uzyskać kilka istotnych dla nas in-
formacji. Obliczamy długość ścieżki (wielkość
ta zostaje zapisana w zmiennej l), dodając osiem
Wersja statyczna programu
Kompilacja omawianego programu przebiega w typowy sposób. Trzeba pamiętać, aby w
systemie dostępna była biblioteka S-Lang . Poszczególne pliki programu możemy kompilo-
wać w następujący sposób (zakładamy, że pliki źródłowe znajdują się w podkatalogu src ):
gcc -c ./src/fs_oper.c -I./src
Budowa wersji binarnej programu, sprowadza się do wydania polecenia:
gcc -o minicmd dialogs.o fList.o fs_oper.o mainfi le.o workscr.o -lslang
Jednak tak powstały program będzie wymagał w systemie obecności biblioteki S-Lang . W
dość prosty sposób możemy usunąć to ograniczenie, tworząc program z użyciem biblio-
tek statycznych. Jednakże oprócz biblioteki S-Lang trzeba także dołączyć bibliotekę Ncur-
ses , w następujący sposób:
O autorze
gcc -o minicmd_static dialogs.o fList.o fs_oper.o mainfi le.o workscr.o /usr/
lib/libslang.a /usr/lib/libncurses.a
Autor zajmuje się tworzeniem oprogramo-
wania dla WIN32 i Linuksa. Zainteresowa-
nia: teoria języków programowania oraz
dobra literatura.
Kontakt z autorem: autorzy@linux.com.pl .
Uzyskana wersja binarna jest znacznie większa niż wersja niestatyczna, ale program dzia-
ła poprawnie w systemach, gdzie nie ma zainstalowanej biblioteki S-Lang .
www.lpmagazine.org
59
439032723.005.png 439032723.001.png
Zgłoś jeśli naruszono regulamin