Jėzyk C w praktyce, cz.2 „ Ž(LeMUr/Fire & blabla) ŽNa poczātek maīy wstėp (jak to ostatnio w kaūdym moim artykule bywa). No i mamy drugā czėōź... A myōlaīem, ūe nie bėdzie dalszego ciāgu. Jeōli dobrze pójdzie to i w tym numerze bėdzie czėōź trzecia, ale to tylko przypuszczenie. Dlaczego miaīo nie byź tego artykuīu? A dlatego, ūe nie wiedziaīem o czym pisaź! Jednak od czego ma siė przyjacióī (hi Thufor ;^)?! Miaīo byź o czymō innym (sam nie wiedziaīem o czym), a nasz kochany Darek (hihi) zaproponowaī dwa tematy. Zajmė siė teraz pierwszym, a nastėpny w nastėpnym odcinku (jak dobrze pójdzie). … Wczytywanie pliku do pamiėci. Zadanie jest bajecznie proste! Jednak wbrew pozorom nieco trzeba pomyōleź. Przydadzā siė tutaj informacje z poprzedniej czėōci - jeōli nie masz Buma #5 to masz problem. Napisz do mnie lub (lepiej) CEDa i przyōlij dysk. W odpowiedzi dostaniesz odpowiedni numer Bumcia (u CEDa moūna "zaīatwiź" sobie wszystkie). Koļczny z tymi pierdoīami i do roboty! ŽŪeby czegoō siė nauczyō potrzeba przykīadu - takie jest moje zdanie. Samo wczytanie pliku do pamiėci nic nam nie da, bo tego nie zobaczymy bezpoōrednio. Moūna trochė to rozbudowaź - napiszmy wiėc nasz odpowiednik kolejnej waūnej (ostatnio byī DIR, pamiėtacie?) komendy systemowej - COPY. Oczywiōcnie nieco mniej rozbudowana, ale doskonale speīniajāca swoje zadanie! Przeanalizujmy problem. Plik trzeba wczytaź do pamiėci, a potem go nagraź na dysk. Proste, ale to nie wszystko. Niestety koderzy zapominajā o "legalnym" przydzielaniu pamiėci (gīównie w demach i intrach, chociaū to powoli (?) siė zmienia), a jest to tutaj bardzo potrzebne. Kurde, pecety nie majā aū takich problemów - zero wielozadaniowoōci - nic nie korzysta z pamiėci w tym samym czasie... ;)) Przed wczytaniem pliku do RAMu trzeba ten RAM zajāź. Jak? Bardzo prosto! ŽOczywiōcie wywoīujemy odpowiedniā funkcjė z odpowiednimi parametrami :-) Chodzi o AllocMem() z exec.library. Oto "skīadnia": … blok = AllocMem(rozmiar, atrybuty) D0 D0 D1 i dla "C-manów" :-) … void *AllocMem(ULONG, ULONG) Czas na tīumaczenie: Žblok - adres poczātkowy przydzielonego bloku pamiėci lub zero gdy pamiėź nie zostanie przydzielona. Trzeba ZAWSZE sprawdziź, czy AllocMem() nie zwróciī zera, bo jeōli tak byīo to NIE WOLNO w ūadnym wypadku uūywaź tej pamiėci! (pamiėź nie zostanie przydzielona, a adres $0 to poczātek CHIP - tam znajdujā siė wektory przerwaļ, baza exec.library i inne waūne dla systemu dane. Jeōli nie wierzysz to spróbuj np. w Asm-One "wyedytowaź" pierwsze komórki RAMu (opcja "N 0"), a potem wpisz kilka pierdóī...) Ž rozmiar - iloōź bajtów, które chcemy zajāź. System automatycznie zaokrāgli podanā liczbė do wielokrotnoōci "system memory chunk size" (no wīaōnie ;). Nie naleūy zajmowaź (a wiėc podawaź tutaj wartoōci) zero bajtów! To nie ma sensu, a tylko "miesza" w danych systemu. Róūne debuggery informujā o takich "przydziaīach". Žatrybuty - dane okreōlajāce rodzaj pamiėci i jej "opcje". Jeōli nie podamy tutaj konkretnej wartoōci (czyli podamy zero) system sam zadba o pamiėź. Na maszynach z Fastem dostaniemy w pierwszej kolejnoōci Fast! „ Wszelakich atrybutów jest trochė: MEMF_CHIP - przydzielona zostanie pamiėź CHIP. Takiej pamiėci wymagajā (flaga _musi_ byź ustawiona) dane ekranów, obrazów (images), dświėki, copper-listy, "duszki" (sprites) i bufory trackdisk.device w wersji systemu wczeōniejszej niū v36. MEMF_FAST - tutaj moūna upchaź wszystkie inne dane. Ale uwaga - NIE wolno nam przydzielaź pamiėci FAST jeōli nie mamy pewnoōci, ūe taka pamiėź istnieje (tzn. pamiėź nie zostanie przydzielona)! (jak to sprawdziź - za chwilė). Tej flagi nie wolno īāczyź z MEMF_CHIP! MEMF_PUBLIC - tej pamiėci _musisz_ uūywaź, gdy korzystasz z pamiėci w przerwaniach lub innych zadaniach (tasks). Pamiėci tej teū trzeba uūyź przy korzystaniu np. z trackdisk.device. MEMF_LOCAL - pamiėź, która nie ginie po resecie procesora. Bardzo przydatna przy pisaniu wirusów lub innych rezydendów. Na systemach niūszych niū v36 moūe nie istnieź i wtedy AllocMem() zwróci 0. MEMF_24BITDMA - pamiėź, która moūe znajdowaź siė na kartach Zorro-II i jest poza zasiėgiem 24-bitowego adresowania. Przydatna przy korzystaniu z DMA kart Zorro-II. Na v36 i mniej moūe nie istnieź. MEMF_KICK - jeszcze lepsza niū MEMF_LOCAL do pisania wirusów! Pamiėź ta jest zajmowana (tzn. system "uzupeīnia" sobie odpowiednie "tabelki") podczas lub nawet przed wywoīaniem procedur z wektorów KickMem i KickTags. Ale niestety (dla autorów wirusów) dostėpna na 100% od v39 - wczeōniejsze wersje systemu mogā jej nie mieź. Jeszcze tylko opcje, które moūna īāczyź z powyūszymi flagami (np. "wyczyszczony CHIP" to "MEMF_CHIP | MEMF_CLEAR"): MEMF_CLEAR - zajėta pamiėź zostanie odrazu wyczyszczona (zapeīniona zerami). ŽMEMF_REVERSE - pamiėź jest zajmowana "od tyīu", tzn. zwrócony (jeōli w ogóle) zostanie najwiėkszy moūliwy adres. Najpierw jest przeszukiwana pamiėź FAST (o ile istnieje). Nowa flaga od v36, ale do v39 ma bīėdy. Wiėcej danych znajdziesz w includzie - jest jeszcze "pamiėź" MEMF_ANY, czyli dowolna. AllocMem() ma pewnā wadė - jeōli lista pamiėci jest uszkodzona to system "spanikuje" komunikatem ŽAN_MemCorrupt, czyli $01000005 (takie maīe Guru). Nie wolno uūywaź AllocMem() w przerwaniach! Jeszcze tylko jedna funkcja, która pozwoli sprawdziź iloōź wolnej pamiėci. Jest to: … rozmiar = AvailMem(atrybuty) D0 D1 czyli … ULONG AvailMem(ULONG) Žatrybuty - takie same jak w AllocMem() plus MEMF_LARGEST jeōli chcemy wiedzieź jaki jest najwiėkszy blok (jest to najwolniejsza operacja) Žrozmiar - iloōź wolnej pamiėci danego typu (atrybuty) lub wielkoōź najwiėkszego jej bloku (jeōli tego chcemy). Weś poprawkė na multitasking - po (lub nawet w trakcie!) sprawdzeniu wielkoōź moūe okazaź siė nieaktualna! Coō moūe zajāź lub zwoliniź czėōź pamiėci... Ž Ale nawaliīem! Ale kiedyō i tak trzeba by byīo to napisaź... Wracajmy do Žtematu. Skād mamy wiedzieź ile pamiėci potrzeba na nasz plik (pamiėtacie? - mamy kopiowaź pliki :-)? To proste - takie dane moūna "wyciāgnāź" ze struktury FileInfoBlock, którā opisaīem w poprzednim odcinku. Ma ona jak zapewne pamiėtacie pole fib_Size zawierajāce dīugoōź pliku. To wīaōnie z niego dowiemy siė ile trzeba zajāź. Ale - po odczytaniu tej danej (w moim przykīadzie o tym zapomniaīem! Sorry...) trzeba musowo sprawdziź czy nie jest to zero (plik moūe mieź takā dīugoōź) - nie wolno przecieū zajmowaź takiej iloōci pamiėci! Wartoōź z fib_Size odczytujemy przez "zaīoūenie locka" na plik, a potem Examine() na tym pliku i mamy wypeīnionā strukturė FileInfoBlock. Po zajėciu pamiėci trzeba wczytaź do niej plik. Najpierw trzeba go jednak otworzyź. Do tego mamy Open() z dos.library. … plik = Open(nazwa, tryb) D0 D1 D2 … BPTR Open(STRPTR, LONG) Žplik  - "uchwyt" do pliku - unikalna wartoōź dla kaūdego otwartego pliku. Jest to wskaśnik jėzyka BCPL Žnazwa - nazwa pliku (wskaśnik do niej) zakoļczona '\0' (NULL) Žtryb - caīkowita wartoōź okreōlajāca po co otwieramy plik:  MODE_OLDFILE - plik otwarty do czytania  MODE_NEWFILE - plik odtwarty do zapisu; jeōli nie istenieje to zostanie stworzony; jeōli istnieje to jego wczeōniejsza wartoōź zostanie skasowana Jako nawzė mozna poda konkretny plik, ale teū np. "NIL:" lub "*" (aktualne okno; nie powinno byź uūywane do v36 (zastėpczo "CON:")). Jeōli plik nie moūe byź z jakiegoō powodu otwarty to zwróconā wartoōciā bėdzie zero, a kod bīėdu moūna odczytaź z IoErr(). IoErr() to funkcja z dos.library, która zwraca kod bīėdu w postaci liczby. Caīa masa róūnych funkcji zwraca zero jeōli wystāpi bīād - tutaj mamy moūliwoōź sprawdzenia co to za bīād. … byk = IoErr() D0 … LONG IoErr(void) Kody bīėdów to osobna historia i moūe napiszė o tym kiedyō wiėcej (sorrki, ale juū za duūo odbiegam od tematu) - na razie obejrzyjcie inkluda . Maīe podsumowanko - mamy otwarty plik i zajėtā pamiėź. Teraz tylko trzeba wczytaź dane z pliku do pamiėci. Robi siė to przez Read() z dos.library. … przeczytanych = Read(plik, bufor, dīugoōź) D0 D1 D2 D3 … LONG Read(BPTR, void *, LONG) Funkacja wczytuje do pamiėci (pod adres 'bufor') 'dīugoōź' bajtów z pliku o "uchwycie" 'plik'. Zwraca liczbė przeczytanych bajtów. Proste, nie?! Ž plik - wartoōź z Open() Ž bufor - adres (wskaśnik do niczego = "APTR" = "void *") wolnej (zajėtej) pamiėci, np. zwrócony a AllocMem() Ž dīugoōź - iloōc potrzebnych bajtów Jeōli Read() zwróci zero to oznacza to koniec pliku. Natomiast wartoōź -1 oznacza bīād (jaki? - to moūna odczytaź z IoErr()). Oczywiōcie dane sā czytane od aktualnej pozycji w pliku (jak jā siė zmienia innym razem). Wczytaliōmy dane, teraz zamknijmy plik śródīowy. Robimy to przez Close(). To bardzo przyjemna funkcja, bo wymaga jedynie "uchwytu do pliku" zwróconego przez Open(). Oczywiōcie jest ona w dos.library. … sukces = Close(plik) D0 D1 … BOOL Close(BPTR) Ž plik - wartoōź z Open() Ž sukces - udaīo siė czy nie? Jeōli tak (TRUE) to OK, ale jeōli nie (FALSE) to trzeba zakoļczyź wszystkie operacje z plikiem (np. buforowany odczyt). Wartoōź ta _nie_ istnieje do v36! Waūne - plik moūna (trzeba przed wyjōciem z programu) zamknāź tylko raz! Po tym wszystkim otwieramy nowy plik - ten do którego "nagrywamy" dane. To proste - Open() z MODE_NEWFILE. Potem trzeba dane zapisaź z bufora do pliku - Write() z dos.library. Write() ma identyczne parametry jak Read(): … zapisanych = Write(plik, bufor, dīugoōź) D0 D1 D2 D3 … LONG Write(BPTR, void *, LONG) Na koniec zamykamy plik wyjōciowy i zwalniamy pamiėź. To ostanie robimy przez FreeMem() z exec.library. Jej zadanie to dopisanie do listy pamiėci zwolnionego obszaru. … FreeMem(blok, rozmiar) A1 D0 … void FreeMem(void *, ULONG) Žblok - adres paiėci (np. zwrócony przez AllocMem()) Žrozmiar - wielkoōź w bajtach (zaokrāglana tak jak w AllocMem()) Nie wolno zwalniaź zera bajtów. Nie wolno zwalniaź nie przydzielonej pamiėci (takie robienie równa siė prawdopodobnie Guru ŽAN_MemCorrupt = Ž$01000005). Nie wolno zwalniaź dwa razy danej pamiėci (= GuruŽ AN_FreeTwice = $01000009). Ponoź przyszīe wersje majā byź bezpieczniejsze. No - skoļczyīem opis funkcji, teraz czas na gotowy program (sprawdzany pod Sas/C 6.50) (śródīówka powinna byź teū w osobnym pliku): „#include „#include „#include „#include „int main(int argc, char *argv[]) „{ „ BPTR file; „ BPTR lock; „ APTR bufor; „ struct FileInfoBlock __aligned fib; /* sprawdzenie liczby argumentów */ „ if(argc!=3) „ { „ Printf("Zīe argumenty!\n"); „ exit(5); „ } /* lock na śródīo */ „ if(!(lock=Lock(argv[1], ACCESS_READ))) „ { „ LONG a; „ a=IoErr(); „ Printf("Byk = %d\n", a); „ Printf("Unable to lock \"%s\"\n", argv[1]); „ exit(10); „ } /* "próbujemy" śródīo */ „ if(!Examine(lock, &fib)) „ { „ LONG a; „ a=IoErr(); „ Printf("Byk = %d\n", a); „ Printf("Unable to examine \"%s\"\n", argv[1]); „ UnLock(lock); „ exit(30); „ } /* przydziaī pamiėci */ „ if(!(bufor=AllocMem(fib.fib_Size, MEMF_CHIP))) „ { „ Printf("Unable to allocate memory!\n"); „ UnLock(lock); „ exit(40); „ } /* otwieramy śródīo */ „ if(!(file=Open(argv[1], MODE_OLDFILE))) „ { „ Printf("Unable to open file \"%s\"\n", argv[1]); „ FreeMem(bufor, fib.fib_Size); „ UnLock(lock); „ exit(50); „ } /* czytamy, zamykamy i odblokowywujemy :) */ „ Read(file, bufor, fib.fib_Size); „ Close(file); „ UnLock(lock); /* otwieramy cel */ „ if(!(file=Open(argv[2], MODE_NEWFILE))) „ { „ Printf("Unable to open file \"%s\"\n", argv[2]); „ FreeMem(bufor, fib.fib_Size); „ exit(60); „ } /* zapisujemy i zmykamy */ „ Write(file, bufor, fib.fib_Size); „ Close(file); /* zwalniamy pamiėź */ „ FreeMem(bufor, fib.fib_Size); „ exit(0); „} Teraz maīe wyjaōnienie - program nie jest napisany tak jak trzeba, tzn. moūna go napisaź na kilka sposobów, a najlepszy to wg. mnie wszystko w jednym "ifie" z "podifami" :-). Program (jak juū wspomniaīem) nie sprawdza dīugoōci pliku (moūe byź Guru przy plikach o zerowej dīugoōci!). Funkcja Printf() to to samo co printf(), ale "na systemie" przez co kod wynikowy jest o kilka bajtów krótszy, Niestety dziaīa to tylko na OS2++; tak wiėc na 1.3-- musicie napisaź po prostu "printf" i dodatkowo doīāczyź . ŽTo wszystko.Ž Mam nadziejė, ūe za duūo nie namieszaīem... W numerze jeszcze maīy dodatek z opisem kilku funkcji dotyczācych obsīugi plików i pamiėci. Nastėpny odcinek o... nie wiem! Byź moūe o "pull-down menu" - na proōbė Thufora. Jeōli TY masz jakāō proōbė (dotyczācā tego cyklu), to dawaj ōmiaīo lista! Postaram siė napisaź coō na zaproponowany temat... … LeMUr/Fire & blabla (6.07.1995)