C dla każdego (cz. 22.)
DOS
To już ostatni odcinek z serii o bibliotece
"dos.library". Biblioteka ta zawiera masę
przeróżnych funkcji. Dziś omówimy pokrótce kilka
najbardziej przydatnych.
Pattern Matching - dopasowywanie do wzorca
Wzorców używamy zwykle w systemie AmigaOS do
oznaczenia zbioru plików, choć nie jest to jedyne
możliwe ich wykorzystanie. Przypomnijmy może
jednak najpierw znaczenie znaków specjalnych,
które mogą występować we wzorcach;
? - zastępuje dowolny, pojedynczy znak;
# - dowolna liczba wystąpień następującego po nim
wzorca;
(ab|cd) - alternatywa wzorców;
~ - negacja logiczna;
[abc] - jeden znak ze zbioru;
[~ef] - jeden znak spośród dopełnienia zbioru [ef];
a-z - skrócony zapis zbioru znaków: oznacza znaki
od "a" do "z";
% - oznacza napis pusty (przydatne np. przy
alternatywie wzorców);
' - synonim "#?", czyli dowolna liczba dowolnych
znaków. Symbol ten jest dostępny od systemu 2.0,
ale jest standardowo wyłączony. Istnieje wiele
programów, które go włączają, np. togglewc,
WildStar, DOSPrefs (wszystkie można znaleźć w
katalogu "utit/boot" na Aminecie). Dla tych,
którzy tymi programami nie dysponują, drobna
wskazówka:
struktura "RootNode", pole "rn_Flags", flaga
"RNF_WILDSTAR".
Kilka przykładów:
A#[a-zA-Z].(c|h) - wszystkie napisy, zawierające
litery alfabetu łacińskiego, które zaczynają się
od litery "A" i kończą na ".c" lub ".h".
~(#?(.o| .info)) - wszystkie napisy oprócz
kończących się na ".o" lub ".info".
Istnieją dwie grupy funkcji, zajmujących się
dopasowywaniem do wzorca.
Pierwsza z nich zawiera funkcje generalnego
stosowania: można je wykorzystywać do dowolnych
celów. Zaczynamy od "tokenizacji", czyli
tłumaczenia wzorca na bardziej zrozumiałą dla
algorytmu formę. Zrobią to za nas funkcje:
LONG ParsePattern( STRPTR pat, STRPTR buf, long
buflen );
LONG ParsePatfcernNoCase( STRPTR pat, STRPTR but,
long buflen );
Jako argument "pat" podajemy wzorzec w opisanej
wcześniej formie. Efektem działania funkcji jest
wpisanie przekonwertowanego wzorca do podanego
jako argument "buf" bufora. Argument "buflen"
określa długość bufora - powinna ona wynosić
2*strlen(pat)+2.
Funkcja zwraca -1 w wypadku błędu, O - gdy we
wzorcu nie znaleziono żadnych znaków specjalnych,
1 - gdy wszystko przebiegło zgodnie z
oczekiwaniami.
Funkcja ParsePatternNoCase() różni się od swojej
siostry tym, że utożsamia duże i małe litery.
"Przetłumaczony" wzorzec można porównać z
interesującym nas napisem za pomocą funkcji:
BOOL MatchPatternf STRPTR pat, STRPTR str);
BOOL MatchPatternNoCase( STRPTR pac, STRPTR str);
Jako argument "pat" podajemy wzorzec
przetłumaczony przez, odpowiednio, ParsePattern()
lub ParsePatterNoCase(). Jako argument "str"
podajemy napis, który próbujemy dopasować do
wzorca.
Funkcja zwraca O, gdy napis nie pasuje do wzorca
lub gdy wystąpił błąd. Błąd ten oznacza zwykle
brak stosu - funkcje te są wysoce rekurencyjne i
powinno się im zapewnić przynajmniej 1500 bajtów
stosu.
Funkcje z drugiej grupy dopasowujących do wzorca
są zorientowane na operacje na plikach.
Umożliwiają one filtrowane listowanie katalogów.
LONG MatchFirst( STRPTR pat, struct AnchorPath
*anchor );
Funkcja MatchFirst() inicjalizuje cały proces. Jako
parametr "pat" podajemy wzorzec, jako "anchor" -
wskaźnik na obszar pamięci, który musi być
podzielny przez 4. Struktura "AnohorPath" jest
zdefiniowana w pliku "dos/dosasl.h". Należy
prawidłowo zainicjować pola "ap_BreakBits" i
"ap_Flags". W to pierwsze wstawiamy maski bitowe
sygnałów, które mają powodować przerwanie procesu,
np. S1GBREAKF_CTRL_C, aby użytkownik mógł przerwać
proces, naciskając [CtrIHC]. W to drugie wstawiamy
odpowiednie flagi, np. APF_DODIR, aby listowane
były także wnętrza katalogów.
Funkcja zwraca (uwaga!) 0, gdy wszystko poszło
dobrze, lub numer błędu (nie ma potrzeby
wywoływania loErr()).
Informacje o znalezionym obiekcie pobieramy z pola
"ap_Info", będącego strukturą "FileInfoBlock".
Zatrzask na katalogu, w którym się ten obiekt
znajduje, uzyskujemy z pola "ap_Current->an_Lock".
LONG MatchNext( struct AnchorPath *anchor );
Funkcja MatchNext() powoduje przejście do
kolejnego obiektu o nazwie pasującej do wzorca.
Funkcja zwraca O, gdy wszystko jest w porządku,
lub numer błędu.
void MatchEnd( struct AnchorPach *anchor);
Funkcję MatchEnd() wywołujemy, gdy MatchNext()
zakończy już pracę, zwracając informację o
błędzie. Funkcja ta powoduje zwolnienie
przydzielonych wcześniej zasobów.
Dzielenie i łączenie
Podczas używania "dos.library" niezwykle przydatne
okazują się funkcje separujące nazwę katalogu od
nazwy pliku oraz łączące nazwy katalogów i plików.
Na pierwszy rzut oka problem wydaje się trywialny.
Przy bliższym zapoznaniu się z nim okazuje się
jednak, że poprawna obsługa wymaga przynajmniej
kilku linijek w "C" - lepiej więc chyba zrobić to
w jednej, wywołując wyspecjalizowaną funkcję.
Zacznijmy od funkcji "dzielących":
STRPTR FilePart( STRPTR path );
STRPTR PathPart( STRPTR path );
FllePart() - zwraca wskaźnik na początek
ostatniego komponentu ścieżki "path", a więc
zwykle nazwy pliku. Jeśli "path" składa się tylko
z jednego komponentu, np. "Listing.c", to
FilePart() zwróci wskaźnik na jego początek.
PathPart() - zwraca wskaźnik na znak następujący
po przedostatnim komponencie napisu oznaczającego
ścieżkę. Poza tym szczegółem zachowuje się tak
samo, jak FilePartO. Przykłady:
Dla "SYS:Devs/DOSDrivers/Pipe" FilePart() zwraca
wskaźnik na "P", a PathPartO na ostatni znak "/".
Dla"C:List" zarówno FilePart(), jak i
PathPart()zwrócą wskaźnik na "L".
Do łączenia dwóch fragmentów ścieżki służy
funkcja:
BOOL AddParc( STRPTR dirname, STRPTR filename,
unsigned long size);
Pierwszym argumentem jest bufor zawierający
początek ścieżki. Zostanie do niego dołączony
fragment ścieżki wskazywany przez "filename".
Rozmiar bufora podajemy przy użyciu trzeciego
argumentu - "size".
Funkcja zwraca 0, gdy nastąpił błąd (zawartość
bufora "dirname" nie zostanie wtedy zmieniona).
Spójrzmy na chwilę na listing 18. Wypisuje on
nazwy wszystkich obiektów pasujących do podanych w
linii argumentowej wzorców. Pamięć na strukturę
"AnchorPath" przydzielamy z atrybutem
"MEMF_CLEAR", dzięki czemu nie musimy później
żadnych pól inicjować. We wzorcu do funkcji
ReadArgs() występuje nie omówiony przez nas
wcześniej znacznik "/M". Informuje on, że można
podać więcej niż jeden napis. Adresy wszystkich
napisów zostaną umieszczone w zakończonej zerem
tablicy, a adres tej tablicy zostanie umieszczony
we właściwym miejscu w buforze podanym przez nas
jako drugi argument funkcji ReadArgs(). W listingu
użyliśmy nie omówionej wcześniej funkcji:
LONG NameFromLock ( BPTR lock, STRPTR buffer, long len);
Dla podanego jako argument "lock" zatrzasku na
obiekcie funkcja ta wpisuje do bufora "buffer" o
długości "len" pełną nazwę obiektu wraz ze ścieżką
dostępu. Funkcja zwraca 0w wypadku błędu.
Lista urządzeń
Teraz dla odmiany zajmiemy się problemem
listowania zainstalo- wanych w systemie urządzeń.
Spis ten jest dostępny w postaci listy elementów
typu "DosList" (patrz "dos/dosextens.h"),
Nazwę urządzenia można odczytać z pola "dol_Name",
które jest typu BSTR. Jest to napis rodem z BCPL,
wskaźnik typu BPTR na tablicę znaków, w której
pierwszy znak jest długością napisu. Do konwersji
z BPTR na "normalny" wskaźnik służy makrodefinicja
BADDR.
W zależności od wartości pola "dol_Type" mamy do
czynienia z różnymi typami urządzeń, dla każdego z
nich zawartość pola "dol_misc", będącego unią
struktur, jest interpretowana inaczej.
Typ wolumen - zawiera w polu "dol_Type" wartość
DLT_VOLUME, wówczas "dol_misc" należy
interpretować jako strukturę "dol_volume", w
której interesującym nas polem jest:
"dot_VolumeDate" - data utworzenia (inicjalizacji)
dysku.
Typ przypisanie (Assign) - pole "dol_Type" równe
DLT_DIRECTORY, unię interpretujemy jako strukturę
"dol_assign". W tym wypadku pole "dol_Lock"
zawiera zatrzask założony na katalogu, któremu
jest przyporządkowane przypisanie.
Nazwę katalogu, do którego odnosi się przypisanie,
możemy otrzymać z zatrzasku "dol_Lock". Gdy
przypisanie odnosi się do kilku katalogów, z
pomocą przychodzi pole "dol_List", które jest
wskaźnikiem na jednokierunkową listę struktur
"AssignList", zawierającą zatrzaski na kolejnych
katalogach przypisania:
struct AssignList
{
struct AssignList *al_Next;
BPTR al_Lock;
}
Typ urządzenie (fizyczne) - wartość pola
"dol_Type" wynosi DLT_DEVICE, w tym wypadku
"dol_misc" jest strukturą "dol_handler". Nie
będziemy jej tu szerzej omawiać, wspomnimy tylko,
że pole "dol_Startup" zawiera wskaźnik BPTR na
strukturę "FileSysStartupMsg". Ta z kolei
struktura w polu "fssm_Environ" zawiera wskaźnik
BPTR na strukturę "DosEwec", w której znajduje się
wiele interesujących informacji na temat geometrii
urządzenia, np. liczba cylindrów, głowic, jak
również identyfikator systemu zapisu.
Wiemy już, co zawiera systemowa lista urządzeń,
ale nie wiemy, skąd ją wziąć. Na to pytanie
odpowiada poniższa funkcja:
struct DosList *AttemptLockDosList ( unsigned long
flags );
Zwraca ona wskaźnik na poszukiwaną przez nas
listę. Jej pierwszy element nie zawiera jednak
ważnych danych. Gdy dostęp do listy nie jest
możliwy (zazwyczaj chwilowo, np. gdy jest
instalowany nowy wolumen), funkcja zwraca NULL.
Argumentem tej funkcji jest tryb dostępu do listy.
W naszym wypadku będzie to flaga "LDF_READ",
deklarująca odczyt listy, w połączeniu z flagą
określającą kierunek naszych poszukiwań:
"LDF_DEVICES" - urządzenia, "LDF_VOLUMES" -
wolumeny, "LDF_ASSIGNS" - przypisania,
Do poruszania się w obrębie listy służy funkcja:
struct DosList *NextDosEntry( struct DosList
*dlist, unsigned long flags );
Jej argumentami są wskaźnik na uzyskany poprzednio
element listy oraz typ poszukiwanego elementu.
Gdy interesuje nas konkretny element listy,
zamiast szukać go samodzielnie, możemy zlecić to
funkcji:
struct DosList *FindDosEntry( struct DosList
*dlist, STRPTR name, unsigned long flags );
Wyszuka ona dla nas pierwszy element o
wyspecyfikowanej nazwie i typie. Jeśli nic nie
znajdzie, zwróci NULL. Funkcję tę można uruchomić
w pętli, gdy zamierzamy znaleźć np. wszystkie
wolumeny o podanej nazwie. Wielkość liter nazwy
nie jest istotna, jednak należy pamiętać, że nazwa
nie może zawierać znaku ":".
Gdy lista urządzeń nie jest nam już potrzebna,
należy poinformować o tym system za pomocą
funkcji:
void UnLockDosListf unsigned long flags );
Jej argumentem jest flaga, której wartość musi być
identyczna, jak podczas przydzielania listy.
Z każdą listą udostępnianą nam przez system należy
się spieszyć, ponieważ ktoś może jej potrzebować.
W zasadzie można by powiedzieć, że wypisywanie
listy na konsolę w listingu 19. nie jest zbyt
właściwym postępowaniem. Należałoby dane
zapamiętać, listę zwolnić, a dopiero później
wypisać rezultaty. To jednak tylko przykład.
Listing nr 18
Listing nr 19