XVIII. |
JEDNOTKA DOS |
Jednotka Dos implementuje řadu podprogramů na bázi rutin operačního systému, především pro práci se soubory na disku. Tato kapitola uvádí jejich vybranou podmnožinu.
XVIII.1. |
Ošetření chybových stavů |
Obsahuje číslo chyby, vrácené operačním systémem při posledním volání některého z podprogramů jednotky Dos. Nejčastější hodnoty jsou:
kód | popis chyby | |||||||
---|---|---|---|---|---|---|---|---|
0 | žádná chyba, vše v pořádku | |||||||
2 | soubor nenalezen | |||||||
3 | cesta nenalezena | |||||||
5 | přístup zakázán | |||||||
8 | nedostatečná paměť | |||||||
18 | další soubory neexistují |
XVIII.2. |
Ovládání systémových hodin |
procedure GetTime (var Hod, Min, Sec, Sec100: Word);
procedure SetTime (Hod, Min, Sec, Sec100: Word);
Procedura GetTime resp. SetTime čte resp. nastavuje aktuální čas, vyjádřený čtveřicí celočíselných parametrů: Hod (hodina, 0 .. 23), Min (minuta, 0 .. 59), Sec (sekunda, 0 .. 59) a Sec100 (setina sekundy, 0 .. 99). Pokus o nastavení neplatného času, kdy je hodnota některého z parametrů mimo uvedený rozsah, nemá žádný efekt.
procedure GetDate (var Rok, Mes, Den, DenTydne: Word);
procedure SetDate (Rok, Mes, Den: Word);
Procedura GetDate resp. SetDate čte resp. nastavuje aktuální datum, vyjádřené čtveřicí resp. trojicí celočíselných parametrů: Rok (rok, 1980 .. 2099), Mes (měsíc, 1 .. 12), Den (den, 1 .. 31) a DenTydne (pořadí dne v týdnu, 0 .. 6, kde 0 je neděle). Pokus o nastavení neplatného data (například 29.2.1993) nemá žádný efekt.
{======================================================================} program DateTimeInfo; {======================================================================} uses Dos; const Tyden : array [0..6] of string[7] { názvy dnů, odpovídající } = ('neděle', 'pondělí', 'úterý',{ číslům dnů v týdnu, } 'středa', 'čtvrtek', 'pátek',{ vráceným procedurou } 'sobota'); { GetDate } var Rok, Mes, Den, DenTydne, { rok, měsíc, den } { a pořadí dne v týdnu } Hod, Min, Sec, Sec100 : Word; { hodina, minuta, sekunda } { a setina sekundy } begin { program } GetDate(Rok, Mes, Den, DenTydne); { načtení data a času ze } GetTime(Hod, Min, Sec, Sec100); { systémových hodin } WriteLn(Hod, ':', Min, ':', Sec, ',', { výpis } Sec100, ' hodin, ', Tyden[DenTydne], ' ', Den, '.', Mes, '.', Rok) end. { program }
XVIII.3. |
Atributy diskových souborů |
bit | atribut | |||||||
---|---|---|---|---|---|---|---|---|
0 | pouze pro čtení (read-only) | |||||||
1 | skrytý (hidden) | |||||||
2 | systémový (system) | |||||||
3 | návěští disku (volume label) | |||||||
4 | adresář (directory) | |||||||
5 | archivní (archive) |
Atribut je nastaven, pokud odpovídající bit má hodnotu 1. Například soubor, jehož byte atributů má hodnotu 35 (tj. binárně 00100011), má nastaveny atributy pouze pro čtení, skrytý a archivní.
Význam jednotlivých atributů definuje operační systém. Nastavený atribut pouze pro čtení znemožňuje modifikaci souboru (používá se především pro ochranu souboru před nechtěným smazáním), skrytý umožňuje skrýt soubor před vyhledáním (například příkazem dir), atributem systémový jsou označeny soubory, obsahující operační systém (tento atribut rovněž činí soubor neviditelným při vyhledávání), atribut návěští disku resp. adresář oznamuje, že daná položka není soubor, nýbrž návěští disku resp. adresář, a atribut archivní se používá pro označení datových souborů, určených pro archivaci (příkazem backup operačního systému). Soubory mohou mít nastaveny jen atributy pouze pro čtení, skrytý, systémový a archivní, kdežto adresáře resp. návěští disku mají vždy nastaven pouze atribut adresář resp. návěští disku.
Čtení a modifikaci hodnoty bytu atributů vybraného souboru umožňují procedury GetFAttr a SetFAttr jednotky Dos.
procedure GetFAttr (var F: soubor; var Atribut: Word);
procedure SetFAttr (var F: soubor; Atribut: Word);
Procedura GetFAttr resp. SetFAttr přečte do parametru Atribut resp. nastaví na hodnotu parametru Atribut byte atributů diskového souboru (nebo adresáře), spojeného s proměnnou F (libovolného typu soubor).
const ReadOnly = $01; { 00000001 - pouze pro čtení }
Hidden = $02; { 00000010 - skrytý }
SysFile = $04; { 00000100 - systémový }
VolumeID = $08; { 00001000 - návěští disku }
Directory = $10; { 00010000 - adresář }
Archive = $20; { 00100000 - archivní }
AnyFile = $3F; { 00111111 - libovolný }
Výše uvedené konstanty (deklarované jednotkou Dos) mohou být využity pro snadné nastavení nebo testování hodnoty bytu atributů. Kombinaci dílčích atributů lze získat součtem příslušných konstant. Například hodnotou výrazu ReadOnly + Archive je číslo $21 (binárně tedy 00100001), odpovídající současně nastaveným atributům pouze pro čtení a archivní.
{======================================================================} program TestAtributu; {======================================================================} uses Dos; var Spec : string; { cesta k diskovému soboru nebo adresáři } F : file; { identifikace souboru nebo adresáře } A : Word; { byte atributů } Bin : string [6]; { znaky binární reprezentace atributů } AA, I : Byte; { pomocné proměnné } begin { program } WriteLn; { titulek programu } WriteLn('ATRIBUTY SOUBORU'); WriteLn; Write('soubor : '); { inicializace souboru F } ReadLn(Spec); Assign(F, Spec); GetFAttr(F, A); { pokus o načtení atributů } if DosError = 0 then { byl-li úspěšný } begin AA := A; Write('atributy : '); { výpis binární reprezentace } for I := 6 downto 1 do { šesti významných bitů bytu } begin { atributů } Bin[I] := Chr(AA mod 2 + Ord('0')); AA := AA div 2 end; Bin[0] := #6; Write(Bin); if A and Directory <> 0 then { je-li F adresář } Write(' Directory ') else { je-li F soubor } begin if A and ReadOnly <> 0 then { ReadOnly } Write(' ReadOnly '); if A and Hidden <> 0 then { Hidden } Write(' Hidden '); if A and SysFile <> 0 then { SysFile } Write(' SysFile '); if A and Archive <> 0 then { Archive } Write(' Archive ') end; WriteLn end else WriteLn('chyba ', DosError) { výpis čísla chyby } end. { program }
XVIII.4. |
Prohledávání adresářů |
type SearchRec = record
Fill: array [1..21] of Byte;
Attr: Byte;
Time: LongInt;
Size: LongInt;
Name: string [12]
end;
Name obsahuje jméno nalezené položky (včetně přípony), Size velikost nalezeného souboru v bytech (resp. nulu, pokud je nalezenou položkou adresář nebo návěští disku), Time časový údaj o poslední modifikaci položky (v komprimovaném tvaru) a Attr její byte atributů. Pole Fill slouží pro vnitřní potřeby vyhledávacích rutin operačního systému.
procedure FindFirst (Spec: string; Atribut: Word; var Info: SearchRec);
Vyhledá na disku první výskyt položky, vyhovující vyhledávacímu kritériu, definovanému zadanými hodnotami parametrů Spec a Atribut. Při kladném výsledku hledání je hodnotou proměnné DosError nula a procedura vrací údaje o nalezené položce v parametru Info. Pokud naopak vyhovující položka v prohledávaném adresáři neexistuje (DosError = 18) nebo byla v parametru Spec (viz níže) zadána chybná či neexistující cesta (DosError = 3), je hodnota parametru Info nedefinovaná.
Hodnotou parametru Spec je maska systémové specifikace hledané položky — cesta k prohledávanému adresáři a maska jména a přípony jeho hledané položky, která smí na rozdíl od konkrétního jména a přípony obsahovat i expanzní znaky operačního systému (wildcards) hvězdička a otazník. Například masce 'c:\b??k.*' vyhovují všechny položky kořenového adresáře na disku C, jejichž jméno je tvořeno čtyřmi znaky, z nichž první je písmeno „b“ a poslední písmeno „k“. Dva prostřední znaky a celá přípona jména položky mohou být libovolné.
Hodnotou parametru Atribut je maska bytu atributů hledané položky. Významné jsou pouze atributy skrytý, systémový, adresář a návěští disku (na hodnotách atributů pouze pro čtení a archivní nezáleží). „Obyčejné“ soubory jsou tedy vyhledávány vždy, kdežto vyhledávání „speciálních“ souborů skrytých resp. systémových, adresářů a položky návěští disku je nutno povolit nastavením příslušného atributu v masce.
Při prohledávání kořenového adresáře disku mohou být nalezeny v něm uložené soubory, jeho podadresáře i návěští disku. Položkou ostatních adresářů návěští disku není, zato výčet jejich podadresářů je rozšířen o dvě položky, které kořenový adresář neobsahuje — odkaz na nadřízený adresář (..) a odkaz na kořenový adresář (.).
procedure FindNext (var Info: SearchRec);
Vyhledá další vyhovující položku podle kritéria, definovaného ve volání procedury FindFirst (potřebné údaje jsou při postupném vyhledávání položek průběžně uchovávány a aktualizovány v položce Fill parametru Info. Pokud další vyhovující položka neexistuje, je nastavena hodnota proměnné DosError na 18.
Typický způsob použití vyhledávacích procedur FindFirst a FindNext demonstruje následující schéma:
uses Dos; var Info: SearchRec; ... FindFirst (..., Info); { vyhledání první položky } while DosError = 0 do { dokud je položka nalezena } begin ... { zpracování údajů Info } { o nalezené položce } FindNext (Info) { vyhledání další položky } end; ...
XVIII.5. |
Časový údaj o poslední aktualizaci souboru |
procedure GetFTime (var F: soubor; var DatCas: LongInt);
procedure SetFTime (var F: soubor; DatCas: LongInt);
Procedura GetFTime resp. SetFTime přečte do parametru DatCas resp. aktualizuje jeho hodnotou komprimovaný časový údaj o poslední aktualizaci souboru, spojeného s parametrem F (proměnná libovolného typu soubor). Soubor musí být v okamžiku volání procedury otevřen (jinak není vrácený údaj platný resp. k aktualizaci nedojde).
type DateTime = record
Year, Month, Day,
Hour, Min, Sec: Word
end;
Typ DateTime popisuje strukturu časového údaje o poslední aktualizaci souboru, dekomprimovaného procedurou UnPackTime nebo předávaného ke komprimaci proceduře PackTime.
procedure UnpackTime (Zapak: LongInt; var Rozpak: DateTime);
procedure PackTime (var Rozpak: DateTime; var Zapak: LongInt);
Procedura UnPackTime konvertuje hodnotu vstupního zkomprimovaného časového údaje Zapak na dekomprimovaný tvar, který vrací v parametru Rozpak. Procedura PackTime provádí obrácenou konverzi.
XVIII.6. |
Zpracování systémové specifikace souboru |
type PathStr : string [79]; { úplná syst. specifikace }
DirStr : string [67]; { cesta k adresáři }
NameStr : string [8]; { jméno souboru }
ExtStr : string [4]; { přípona (včetně tečky) }
Jednotkou předdeklarované řetězcové typy pro popis systémové specifikace a jejích částí, které využívají procedury FSplit a FExpand.
procedure FSplit (Spec: PathStr; var Cesta: DirStr;
var Jmeno: NameStr; var Prip: ExtStr);
Rozloží zadanou systémovou specifikaci souboru na adresář, jméno a příponu. Není kontrolována syntax ani existence cesty.
function FExpand (Spec: PathStr): PathStr;
Vrací úplnou systémovou specifikaci souboru, získanou rozšířením zadané specifikace Spec podle skutečné struktury adresářů na disku (chybějící části cesty doplní podle cesty k aktuálnímu adresáři aktuálního disku). Parametr smí obsahovat i expanzní znaky (wildcards) hvězdička a otazník.
{======================================================================} program Dir; {======================================================================} uses Dos; var S : SearchRec; { vyhledávací proměnná } Maska : string; { maska systémové specifikace } { vyhledávaných položek } Adresar : DirStr; { cesta k adresáři } Jmeno : NameStr; { jméno } Pripona : ExtStr; { přípona } T : DateTime; { datum a čas poslední } { aktualizace souboru } begin { program } WriteLn; { prázdná řádka } Maska := ParamStr(1); { převzetí masky z příkazové } if Maska = '' then { řádky resp. nastavení její } Maska := '*.*'; { implicitní hodnoty } FSplit(FExpand(Maska), { rozložení expandované masky } Adresar, Jmeno, Pripona); { na adresář, jméno a příponu } WriteLn('soubory ', { výpis expandované masky } Adresar, Jmeno, Pripona); FindFirst(Adresar + '*.*', VolumeID, S);{ vyhledání návěští disku } if (DosError <> 0) and (DosError <> 18) { vyskytla se nějaká chyba } then { (např. není disk v jednotce)} begin WriteLn('chyba ', DosError, ' !'); { výpis chybového hlášení } Halt { ukončení běhu programu } end; Write('disk v jednotce ', Adresar[1], { výpis úvodní části údaje } ' je '); { o návěští disku } if DosError = 0 then { bylo-li návěští nalezeno } with S do begin Delete(Name, 9, 1); { odstranění tečky přípony } { z návěští } WriteLn(Name); { výpis návěští } end else { DosError = 18 } { nebylo-li návěští nalezeno } WriteLn('nepojmenovaný'); WriteLn; { prázdná řádka } FindFirst(Maska, AnyFile - VolumeID, S);{ vyhledání první vyhovující } { položky } while DosError = 0 do { dokud položka existuje } begin with S do begin Write(Name, '':15 - Length(Name)); { tisk jména položky a mezery } if Attr and Directory <> 0 then { je-li položka adresářem } Write('<DIR>') { tisk označení <DIR> } else { položka je soubor } begin { tisk atributů souboru } if Attr and Archive <> 0 { archivní } then Write('A') else Write('-'); if Attr and SysFile <> 0 { systémový } then Write('S') else Write('-'); if Attr and Hidden <> 0 { skrytý } then Write('H') else Write('-'); if Attr and ReadOnly <> 0 { pouze pro čtení } then Write('R') else Write('-'); Write(' ') { dorovnání na šířku <DIR> } end; Write('':3); { mezera } UnpackTime(Time, T); { dekomprimace časového údaje } { o poslední aktualizaci } with T do begin { zarovnaný výpis údaje } if Day < 10 then Write('0'); { datum } Write(Day, '.'); if Month < 10 then Write('0'); Write(Month, '.', Year, '':3); if Hour < 10 then Write('0'); { čas } Write(Hour, ':'); if Min < 10 then Write('0'); Write(Min, ':'); if Sec < 10 then Write('0'); Write(Sec, '':3) end; Write(Size:8, 'B'); { výpis velikosti položky } WriteLn { konec řádky tabulky } end; FindNext(S) { vyhledání další položky } end; { konec cyklu while } if (DosError <> 18) then { vyskytla se nějaká chyba } WriteLn('chyba ', DosError, ' !'); { při vyhledávání položek } WriteLn { prázdná řádka } end. { program }
Příklad výpisu :
soubory C:\*.* disk v jednotce C je SYSTEM DISK IO.SYS -SHR 09.04.1991 05:00:00 33430B MSDOS.SYS -SHR 09.04.1991 05:00:00 37394B COMMAND.COM ---- 09.04.1991 05:00:00 47845B NC <DIR> 09.01.1991 11:31:56 0B SYSTEM <DIR> 20.11.1990 10:16:12 0B UT <DIR> 20.11.1990 10:18:46 0B CONFIG.SYS A--- 23.01.1992 14:07:00 128B BAT <DIR> 08.03.1991 14:43:52 0B NET <DIR> 21.01.1992 11:41:18 0B CONFIG.WIN A--R 24.01.1992 09:49:08 149B CONFIG.VPP A--R 23.01.1992 14:07:00 128B AUTOEXEC.BAT A--- 22.11.1992 22:30:54 144B
XVIII.7. |
Volání přerušení |
Pro tento způsob volání podprogramů a předávání dat je samozřejmě nejjednodušším a nejpřehlednějším způsobem zápisu příslušné části programu použití instrukcí assembleru (Turbo Pascal umožňuje jejich vkládání přímo do zdrojového textu programu). Pro ty, kteří assembler neovládají, poskytuje dostatečně jednoduché a přehledné prostředky k volání přerušení jednotka Dos.
type Registers = record
case Integer of
0: (AX, BX, CX, DX, BP, SI,
DI, DS, ES, Flags : Word);
1: (AL, AH, BL, BH, CL, CH,
DL, DH : Byte)
end;
Typ Registers je implementován pro předávání hodnot registrů procesoru mezi programem a rutinami operačního systému. Vstupní parametry rutin se namísto do registrů procesoru vkládají do stejnojmenných položek pomocné proměnné typu Registers, která se pak použije jako parametr procedury volání přerušení. Procedura zkopíruje hodnoty položek do příslušných registrů procesoru, zavolá požadované přerušení a po jeho skončení zkopíruje případné výstupní hodnoty z registrů do položek parametru, odkud si je může program vyzvednout.
Položky druhé varianty záznamu umožňují ovládání položek AX, BX, CX a DX první varianty (a potažmo datových registrů ax, bx, cx a dx procesoru) po jednotlivých bytech. Například AL resp. AH je nižší resp. vyšší byte položky AX atd.
procedure Intr (Cislo: Byte; var R: Registers);
Naplní registry procesoru hodnotami položek parametru R, vyvolá přerušení číslo Cislo a po jeho skončení naplní položky parametru R aktuálními hodnotami registrů procesoru.
procedure MsDos (var R: Registers);
Pracuje podobně jako Intr, avšak místo obecného přerušení volá speciálně přerušení $21, tzv. „služby operačního systému“. Volání MsDos (R) je tedy ekvivalentní volání Intr ($21, R).
{======================================================================} program DemoIntr; {======================================================================} uses Dos; procedure Videomod (VM: Byte); { nastaví videomód displeje podle } {----------------------------} { hodnoty parametru VM } var R: Registers; begin { Videomod } R.AH := 0; { výběr funkce: nastav videomód } R.AL := VM; { výběr videomódu } Intr($10, R) { volání funkce } end; { VideoMod } function VerzeDOS: string; { vrací řetězec identifikačního } {------------------------} { čísla verze DOSu } var R : Registers; Verze, { řetězcový tvar čísla verze } Subverze : string [1]; { řetězcový tvar čísla subverze } begin { VerzeDOS } R.AH := $30; { výběr služby: vrať číslo verze } MsDos(R); { volání služby } Str(R.AL, Verze); { převod vrácených čísel hlavní } Str(R.AH, SubVerze); { verze a subverze na řetězce } VerzeDOS := Verze + '.' + Subverze { sloučení do výstupního řetězce } end; { VerzeDOS } begin { program } Videomod(0); { textový mód 25 řádek x 40 znaků } Writeln('aktuální videomód: ', 0); { výpis čísla videomódu a verze } WriteLn('verze DOSu: ', VerzeDOS); { operačního systému } Writeln; Write('Stiskni ENTER:'); ReadLn; { čekání na stisk klávesy ENTER } Videomod(3); { textový mód 25 řádek x 80 znaků } Writeln('aktuální videomód: ', 3); { výpis čísla videomódu a verze } WriteLn('verze DOSu: ', VerzeDOS); { operačního systému } Writeln; Write('Stiskni ENTER:'); ReadLn { čekání na stisk klávesy ENTER } end. { program }
XVIII.8. |
Spuštění externího programu |
procedure Exec (Prg, Param: string);
Zavede do paměti spustitelný program, uložený v diskovém souboru se systémovou specifikací Prg a předá mu řízení. Řetězec Param obsahuje případné parametry spouštěného programu ve formátu příkazové řádky operačního systému (pokud spouštěný program parametry nemá, je tento řetězec prázdný).
Po skončení volaného programu nebo neúspěšném pokusu o jeho zavedení vrátí operační systém řízení programu volajícímu. Pokud volaný program nemohl být z nějakého důvodu zaveden, obsahuje proměnná DosError kód příslušné chyby. Typickou příčinou neúspěchu při zavádění programu do paměti je její nedostatečná kapacita (DosError = 8), způsobená absencí omezení velikosti heapu volajícího programu (viz požadavek na přidělení paměti).
Rezidentní příkazy operačního systému (dir, chdir, copy apod.) a dávkové (.bat) soubory lze spouštět pouze jako parametry interpretru příkazů command.com:
Přepínač /C způsobí ukončení činnosti interpretru ihned po provedení zadaného příkazu (v tomto případě příkazu dir *.pas). Pokud by nebyl uveden, ponechá si command.com řízení až do uvedení příkazu k jeho ukončení (exit) z příkazové řádky.Exec ('c:\command.com', '/C dir *.pas')
Operační systém implementuje obslužné rutiny různých událostí, jejichž adresy jsou uloženy v tzv. tabulce vektorů přerušení na začátku operační paměti. Turbopascalské programy však instalují vlastní obsluhu některých událostí — uschovají aktuální hodnotu příslušného vektoru a nahradí ji adresou vlastní obslužné rutiny. Pokud má externí program, spouštěný z prostředí turbopascalského programu, pracovat v původním prostředí DOSu, je nutno před jeho spuštěním procedurou Exec i po jeho skončení zaměnit pomocí procedury SwapVectors hodnoty aktuálních a uložených přerušovacích vektorů.
Poskytne dvoubytovou hodnotu, kterou vrací operační systém po ukončení běhu externího programu, spuštěného předchozím voláním procedury Exec. Nižší byte vrácené hodnoty obsahuje výstupní kód programu a vyšší byte indikuje způsob jeho ukončení:
kód | způsob ukončení programu | |||||||
---|---|---|---|---|---|---|---|---|
0 | normální ukončení | |||||||
1 | povel CTRL+BREAK z klávesnice | |||||||
2 | chyba zařízení | |||||||
3 | ponechán v paměti jako rezidentní |
{======================================================================} program ExecDemo; {======================================================================} {$M 1024, 0, 0} { minimální zásobník, žádný heap } uses Dos; var Prg, { specifikace souboru s programem } Par : string; { specifikace parametrů programu } Vysledek : Word; { hodnota vrácená DosExitCode } begin { program } WriteLn; { titulek } WriteLn('EXECDEMO'); WriteLn; Write('spustit program: '); { načtení specifikace programu } ReadLn(Prg); { jeho parametrů } Write('s parametry : '); ReadLn(Par); SwapVectors; { nastavení vektorů přerušení DOS } Exec(Prg, Par); { pokus o zavedení a spuštění Prg } SwapVectors; { obnova vektorů přerušení } if DosError <> 0 then { došlo při zavádění k chybě? } WriteLn('chyba ', DosError, '!') { ano - výpis čísla chyby } else { ne } begin { výpis hodnoty DosExitCode } Vysledek := DosExitCode; WriteLn('způsob ukončení: ', { - vyšší byte } Hi(Vysledek)); WriteLn('výstupní kód : ', Lo(Vysledek)) { - nižší byte } end; WriteLn end. { program }