17.19. ICVIRG Funkce: Podprogram ICVIRG vyhradí úsek paměti o zadané délce a ztotožní jej se zadanou položkou z LINKAGE SECTION (stejně jako podprogram ICUGETV, viz 17.4.). Navíc však do tohoto úseku paměti načte zadaný soubor (resp. jeho začátek). Způsob volání: CALL 'ICVIRG' USING položka [délka] soubor [dispozice] Pravidla: Položka uvedená za USING musí být popsána v LINKAGE SECTION (nebo teoreticky ve FILE SECTION) s číslem úrovně 01 nebo 77 (srv. 17.4. ICUGETV a 4.9. LINKAGE SECTION). Je-li uveden druhý argument "délka" (jímž musí být celé číslo nebo celočíselná numerická položka nebo speciální index nebo položka s USAGE INDEX s nezápornou hodnotou), zadává explicitně délku úseku paměti, jenž má být vyhrazen (implicitní délka prvního argumentu se pak ignoruje). Není-li druhý argument "délka" uveden, bude vyhrazen úsek paměti v implicitní délce prvního argumentu (uživatel pak musí ovšem zajistit, aby v době provádění příkazu CALL 'ICVIRG' bylo možno délku prvního argumentu vypočítat a aby tato délka měla vhodnou hodnotu). V obou případech nesmí délka úseku přesáhnout 65504 bytů. Argument "soubor" zadává jméno souboru, jenž má být do vyhrazeného úseku paměti načten. Musí to být buďto alfanumerický literál obsahující jméno souboru nebo skupinová, alfanumerická nebo alfanumerická editovaná položka obsahující odleva jméno souboru a za ním byte s nulovou binární hodnotou (případný zbytek položky za touto binární nulou se ignoruje). Jméno souboru musí mít tvar přípustný v používaném operačním systému (přesněji: pro funkci fopen jazyka C), tzn. může leč nemusí obsahovat absolutní nebo relativní cestu a tečku následovanou postfixem (tedy např. 'josef', 'josef.bum', '\usr\petr\jan', 'kaspar\melich\balta.zar' apod.). Poslední argument "dispozice" určuje způsob zpracovávání souboru jednak v podprogramu ICVIRG (jenž provádí jakési otevření souboru) a jednak v podprogramu ICVIRF (jenž provádí jakési uzavření souboru). Existují 4 přípustné dispozice (přičemž vynechání parametru "dispozice" je ekvivalentní s uvedením dispozice 'UPDATE'): 'WRITE' .... Soubor bude vytvářen. Při volání podprogramu ICVIRG se soubor zadaného jména nepředpokládá a nečte; pouze se vyhradí úsek paměti zadané délky s nedefinovaným obsahem. Po návratu z podprogramu ICVIRG nyní uživatel může tento úsek paměti libovolným způsobem naplnit. Pak vyvolá podprogram ICVIRF, jenž vytvoří nový soubor zadaného jména a zapíše do něj okamžitý obsah úseku paměti. (Pokud existoval starší stejnojmenný soubor, bude zrušen.) 'APPEND' ... Soubor bude sekvenčně prodlužován. Pokud dosud neexistoval stejnojmenný soubor, bude průběh přesně stejný jako při dispozici 'WRITE'. Pokud starší stejnojmenný soubor existoval, bude sice pro uživatele situace stejná jako při dispozici 'WRITE' (ICVIRG vyhradí úsek paměti zadané délky s nedefinovaným obsahem a uživatel jej naplní), avšak podprogram ICVIRF nevytváří nový soubor, nýbrž prodlouží dosavadní soubor tak, že za jeho dosavadní konec připojí obsah úseku paměti. 'UPDATE' ... (Nebo neuvedení dispozice.) Soubor bude načten s možností opravování. Musí existovat soubor zadaného jména. Podprogram ICVIRG tento soubor (resp. jeho začátek) načte do vyhrazeného úseku paměti, takže po návratu z podprogramu ICVIRG bude položka uvedená jako první argument za USING obsahovat načtený okamžitý stav souboru. Uživatel nyní může s položkou libovolně pracovat, tj. může její obsah využívat, testovat i měnit. Jakmile vyvolá podprogram ICVIRF, tento podprogram přepíše (opraví) soubor okamžitým obsahem úseku paměti ztotožněného se zadanou položkou. Délku souboru však uživatel nemůže zvětšit ani zmenšit. 'READ' ..... Soubor bude načten bez možnosti opravování. Situace je stejná jako při dispozici 'UPDATE', avšak podprogram ICVIRF nezapisuje obsah úseku paměti zpět do souboru; při uvedení dispozice 'READ' tedy zůstává obsah souboru nezměněn. Uživatel sice může obsah položky měnit, ovšem tyto změny se do souboru nikdy nepromítnou a při vyvolání podprogramu ICVIRF se ztratí i s celým úsekem paměti. (Při dispozici 'READ' je tedy soubor zpracováván přesně stejně jako při příkazu LOAD, pro uvolňování vyhrazeného úseku paměti je však třeba vyvolat podprogram ICVIRF a nikoliv podprogram ICUFREE jako při LOAD.) Při dispozici 'WRITE' nebo 'APPEND' bude vyhrazen úsek paměti o délce rovné hodnotě argumentu "délka" (je-li zadán) resp. implicitní délce prvního argumentu. (Jak bude popsáno v 17.20., může při volání podprogramu ICVIRF zadat menší délku skutečně obsazené části úseku paměti, která se má zapsat na disk.) Při dispozici 'UPDATE' nebo 'READ' se tato explicitně nebo implicitně zadaná délka srovná s plnou délkou zadaného souboru a vyhrazuje se úsek paměti o délce rovné minimu z obou délek: a) Je-li zadaná délka menší než délka souboru, vyhradí se úsek paměti pouze v zadané délce a načte se do něj pouze začátek souboru. Při dispozici 'UPDATE' pak podprogram ICVIRF přepíše pouze začátek souboru o této délce (nelze způsobit, aby se oprava prováděla v jiné než této délce). Přesahující zbytek souboru uživatel nedostává k dispozici a rovněž jej nemůže změnit. (Takto může uživatel postupovat úmyslně v případě, že zbytek souboru je z hlediska tohoto zpracování nezajímavý.) b) Je-li zadaná délka větší než délka souboru, vyhradí se úsek paměti pouze v délce souboru a načte se do něj celý soubor. Přesahující konec zadané položky nedostává přidělenu paměť a nesmí být tedy zpracováván. (Takto uživatel postupuje, chce-li zpracovávat celý soubor, ovšem jeho přesnou délku nezná; zadá tedy explicitně nebo implicitně maximální možnou délku. Musí být pak ovšem schopen z obsahu souboru rozpoznat jeho délku, aby zpracovával položku nejvýše v délce načteného souboru.) Podprogram ICVIRG vyvolá systémovou funkci malloc, čímž vyhradí nový úsek paměti o zadané délce resp. o délce rovné minimu ze zadané délky a délky souboru (viz výše). Adresu takto získaného úseku paměti dosadí do ukazatele přiděleného položce uvedené jako první argument za USING. Takto je tedy tato položka ztotožněna s vyhrazeným úsekem paměti a může být v následujících příkazech libovolně zpracovávána. Jak bylo popsáno výše, její počáteční obsah je při dispozicích 'WRITE' a 'APPEND' nedefinován, zatímco při dispozicích 'UPDATE' a 'READ' je roven obsahu zadaného souboru platnému v okamžiku volání podprogramu ICVIRG. Poznámky: 1) Po úspěšném provedení příkazu CALL 'ICVIRG' lze používat nejen zadanou položku, ale též položky jí podřízené a dále též položky spojené s ní pomocí klauzule REDEFINES a položky jim podřízené. Pokud takové navzájem se redefinující položky mají různé délky, je vhodné uvést v příkazu CALL 'ICVIRG' tu nejdelší z nich nebo zadat maximální délku explicitně; jinak by byl totiž přidělen pouze kratší úsek paměti a přesahující byty delších položek by již neměly přidělenu paměť a nemohly by být tedy zpracovávány. 2) Je-li explicitně nebo implicitně zadaná délka úseku nulová, není to chyba, ovšem žádný úsek paměti se nevyhrazuje a položka nesmí být tedy zpracovávána (došlo by k havárii výpočtu nebo k přemazání paměti přidělené jiným objektům). Přidělený ukazatel není vynulován! 3) Nepodaří-li se systémové funkci malloc úsek paměti zadané délky vyhradit (je-li již vyčerpána celá volná paměť), bude přidělený ukazatel vynulován, avšak nehlásí se žádná chyba, výpočet není ukončen a pokračuje normálně dál. Při pokusu o práci s položkou dojde pravděpodobně k havárii výpočtu. Uživatel si tento stav může po provedení příkazu CALL 'ICVIRG' zjistit způsobem, který je popsán u podprogramu ICUGETV (viz 17.4.). Dojde-li však k jakékoliv chybě při práci se souborem, bude výpočet okamžitě ukončen s hlášením příslušné chyby. 4) Uložení obsahu úseku paměti ztotožněného se zadanou položkou do souboru se provádí (s výjimkou dispozice 'READ') výhradně při explicitním vyvolání podprogramu ICVIRF; na rozdíl od COBOLu DOS-3 tedy nikoliv průběžně během zpracovávání položky ani při ukončení výpočtu příkazem STOP RUN nebo chybou. Vyvolání podprogramu ICVIRF tedy nesmí chybět! (Na druhé straně, zjistí-li se v průběhu výpočtu závažná nepřístojnost, stačí nevyvolat podprogram ICVIRF a původní stav souboru zůstane nezměněn.) 5) Hodlá-li uživatel obsah souboru měnit, uvede při volání podprogramu ICVIRG dispozici 'UPDATE' nebo dispozici vynechá. Doporučuje se pak zvýšená opatrnost, neboť při omylu (nerozpozná-li jej zavčas a vyvolá podprogram ICVIRF) může snadno zničit obsah celého souboru. Při správném průběhu by měl vyvolat podprogram ICVIRF co nejdříve po provedení všech změn v položce, aby mu případná havárie výpočtu nebo počítače v průběhu dalšího výpočtu nezabránila realizovat tyto změny i v souboru. Hodlá-li uživatel obsah souboru pouze využívat (číst) a nikoliv měnit, měl by při volání podprogramu ICVIRG uvést dispozici 'READ', aby nemohl změnit resp. zničit soubor ani v případě omylu. 6) S týmž souborem může současně pracovat i několik uživatelů v multiprogramovém režimu. Synchronizace jejich činnosti je pak pouze jejich záležitostí. 7) Pracuje-li uživatel s určitým souborem pouze prostřednictvím podprogramů ICVIRG a ICVIRF, pak soubor nejprve vytvoří při dispozici 'WRITE' nebo 'APPEND', a takto vytvořený soubor pak může i mnohokrát zpracovávat, přičemž může libovolně střídat prodlužování ('APPEND'), čtení s opravováním ('UPDATE') a čtení bez opravování ('READ'); vždy se začne od toho obsahu souboru, jenž byl výsledkem předešlého zpracování. Je ovšem též možné vytvořit soubor pomocí podprogramů ICVIRG a ICVIRF a zpracovávat jej pak jinými prostředky (třeba cobolskými příkazy OPEN, READ, REWRITE a CLOSE, má-li soubor odpovídající organizaci), anebo naopak vytvořit soubor jinými prostředky (třeba cobolskými příkazy OPEN OUTPUT, WRITE a CLOSE) a zpracovávat jej pak pomocí podprogramů ICVIRG a ICVIRF (zná-li uživatel vnitřní representaci a strukturu souboru). Příklad: Je-li argument "soubor" položkou, nesmí uživatel opomenout uložit do ní za jméno souboru ještě i nulový byte, např. příkazem STRING 'josef.bum' #00# INTO POLOZKA Zjišťuje-li jméno dynamicky, takže je má např. v položce JMENO a jeho délku v položce DJM, může buďto dosadit nulový byte za poslední znak jména v položce JMENO příkazem CALL 'ICMVC' USING #00# 0 JMENO DJM 1 (bylo by možno použít i příkaz STRING, byl by však velmi neefektivní) nebo příkazem UFO '*(' JMENO '+' *DJM ')=0X00;' #0A#. Použití: Pomocí podprogramů ICVIRG a ICVIRF je vhodné vytvářet a zpracovávat nikoliv obyčejný sekvenční soubor tvořený posloupností logických vět, nýbrž např. nějakou převodní tabulku (ceník) zpracovávanou přímo. Organizace takového souboru nemusí být jednou z organizací známých operačnímu systému nebo jazyku COBOL, nýbrž může být zcela libovolná. Případné členění souboru do logických vět je zcela záležitostí uživatele a netýká se operačního systému. Příklad: LINKAGE SECTION. 01 ZAVOD. 02 ZAMESTNANEC OCCURS 1000 INDEXED I. 03 CISLO PIC 9(6) COMP. 03 JMENO PIC X(30). 03 PLAT PIC 9(7) COMP. : CALL 'ICVIRG' USING ZAVOD 'zavsoub.xy'. : IF CISLO(I) = CIS ADD 100 TO PLAT(I). MOVE JM TO JMENO(I). ADD PLAT(I) TO SOUCET-PLATU. : CALL 'ICVIRF' USING ZAVOD. Je-li délka souboru nejvýše 38000 bytů, vyhradí podprogram ICVIRG úsek paměti o délce rovné délce souboru zavsoub.xy a načte do něj celý tento soubor (s přebytkem položky ZAVOD pak uživatel nesmí pracovat). Je-li délka souboru větší než 38000 bytů, vyhradí úsek paměti o délce 38000 bytů a načte do něj prvních 38000 bytů souboru. V obou případech ztotožní vyhrazený úsek paměti s položkou ZAVOD. Položka ZAMESTNANEC (I) je vlastně I-tou logickou větou souboru dat. Přechod na další logickou větu souboru dat se ovšem provádí nikoliv příkazem READ, nýbrž příkazem SET I UP BY 1 resp. ADD 1 TO K apod. Použití: Při zpracovávání souboru pomocí podprogramů ICVIRG a ICVIRF spočívá výhoda především v tom, že máme takto celý soubor dat načten současně do paměti, takže pro práci s libovolnou logickou větou stačí jen zjistit a naplnit její index. Lze tedy např.: 1) Zpracovávat soubor dat přímo, tj. zpracovávat logické věty v libovolném pořadí, aniž bychom museli před zpracováním určité logické věty zpracovat všechny předcházející logické věty. (K tomu ovšem musíme umět určit index hledané logické věty, je-li zadán obsah některé její položky. Musíme si tedy nasimulovat něco na způsob indexové, přímé nebo relativní organizace souboru dat. Např. je velmi jednoduché nasimulovat funkci příkazu START, uschováme-li si do tabulky umístěné na začátku souboru indexy pro ty logické věty, od nichž může začínat zpracování.) 2) Pracovat současně (pomocí indexů) se dvěma nebo více logickými větami, aniž bychom je přesunovali do pomocných položek. 3) Vytvářet organizace dat nezpracovatelné operačním systémem (např. soubor dat složený z několika částí, z nichž každá obsahuje logické věty jiné délky). 4) Simulovat tzv. relativní organizaci, při níž je logická věta přímo dosažitelná zadáním pořadového čísla v souboru dat. Příklad: LINKAGE SECTION. 01 VSEHOCHUT. 02 POCETA PIC S9999 COMP. 02 POCETB PIC S9999 COMP. 02 STARTY OCCURS 10. 03 KLICS PIC S9999 COMP. 03 INDA INDEX. 03 INDB INDEX. 02 TABA OCCURS 1000 DEPENDING POCETA INDEXED IA. 03 KLICA PIC S9999 COMP. 03 UDAJA PIC X(7). 02 TABB OCCURS 2000 DEPENDING POCETB INDEXED IB. 03 KLICB PIC S9999 COMP. 03 UDAJB PIC X(12). : CALL 'ICVIRG' USING VSEHOCHUT 32000 'vse.t' 'READ'. Podprogram ICVIRG vyhradí úsek paměti o délce rovné délce souboru vse.t (nejvýše však 32000 bytů), ztotožní jej s položkou VSEHOCHUT a načte do něj soubor vse.t (resp. jeho prvních 32000 bytů). Uživatel sice smí obsah položky VSEHOCHUT měnit, tyto změny se však při vyvolání podprogramu ICVIRF nepromítnou zpět do souboru vse.t; podprogram ICVIRF pouze uvolní vyhrazený úsek paměti. Příklad: Podnik má 8 závodů s čísly 1,2,...,8. Každý zaměstnanec má šestimístné číslo, jehož první cifrou je vždy číslo závodu. Celkový počet zaměstnanců je nejvýše 999. Máme navrhnout vhodné uložení takového souboru dat, jež umožňuje při zadání čísla závodu z klávesnice vypsat logické věty pro všechny zaměstnance zadaného závodu. Soubor bude obsahovat nejprve celkový počet zaměstnanců (POCET) a pak osm indexů pro první zaměstnance jednotlivých závodů (PRVNIZAM). Pak následují logické věty jednotlivých zaměstnanců, které sice nemusí být setříděny dle svých čísel, musí však být sdruženy po jednotlivých závodech. IDENTIFICATION DIVISION. PROGRAM-ID. ABCD. DATA DIVISION. WORKING-STORAGE SECTION. 77 CZ PIC 9. & zadává se z klávesnice LINKAGE SECTION. 01 PODNIK. 02 POCET PIC S9999 COMP. 02 PRVNIZAM INDEX OCCURS 8. 02 ZAMESTNANEC OCCURS 1000 DEPENDING POCET INDEXED I. 03 CISLO-ZAVODU PIC 9. 03 ZBYTEK PIC X(49). PROCEDURE DIVISION. CALL 'ICVIRG' USING PODNIK 50034 'podn.abc' 'READ'. MOVE 0 TO CISLO-ZAVODU (POCET+1). ZNOVU. MOVE 0 TO CZ. DISPLAY 'ZADEJ CISLO ZAVODU N' UPON CONSOLE. ACCEPT CZ FROM CONSOLE. IF CZ < '0' OR > '8' DISPLAY 'CHYBA' UPON CONSOLE GO ZNOVU. IF CZ = 0 CALL 'ICVIRF' USING PODNIK STOP RUN. SET I TO PRVNIZAM (CZ). DISPLAY NEWPAGE. CHLAP. IF CISLO-ZAVODU (I) = CZ DISPLAY ZAMESTNANEC (I) SET I UP BY 1 GO CHLAP. GO ZNOVU. Ve volání podprogramu ICVIRG je nutno uvést explicitní délku (50034). Při jejím vynechání by se totiž použila implicitní délka položky PODNIK, již nelze na začátku výpočtu vyčíslit, neboť je závislá na hodnotě položky POCET, která nemá dosud přidělenu paměť. Místo uvedení explicitní délky 50034 by bylo též možné redefinovat položku PODNIK položkou pevné délky (01 PODNIK1 REDEFINES PODNIK PIC X(50034).) a tuto pak uvést místo položky PODNIK ve volání podprogramu ICVIRG; po jejím ztotožnění s vyhrazeným úsekem paměti by s ním byla zároveň ztotožněna i položka PODNIK. Za posledního zaměstnance umístíme ještě nulu (jakoby zde byl další zaměstnanec s jinak nepřípustným číslem); bez tohoto opatření by totiž musela podmínka v paragrafu CHLAP znít "IF I <= POCET AND CISLO-ZAVODU(I) = CZ ...". Je ovšem třeba zajistit, aby měl byte CISLO-ZAVODU(POCET+1) přidělenu paměť, takže soubor podn.abc musí mít za posledním zaměstnancem minimálně jeden byte navíc. Konec zpracování nastává v případě, že operátor napíše 0 anebo nenapíše žádný znak a stiskne hned klávesu ENTER. Srovnání "IF CZ < '0' OR > '8' ..." zastupuje zároveň i test numeričnosti položky CZ. Příkaz "SET I TO PRVNIZAM(CZ)" ukládá do I index prvního zaměstnance CZ-tého závodu a má tedy vlastně stejnou funkci jako příkaz START při sekvenčním čtení indexového souboru. Poznámka: Funkce podprogramů ICVIRG a ICUGETV se liší v těchto dvou zásadních bodech: 1) Úsek paměti vyhrazený podprogramem ICUGETV má počáteční obsah nedefinovaný, stejně je tomu i u úseku paměti vyhrazeného podprogramem ICVIRG při dispozici 'WRITE' nebo 'APPEND'. Naproti tomu úsek paměti vyhrazený podprogramem ICVIRG při dispozici 'READ' nebo 'UPDATE' (nebo příkazem LOAD) má počáteční obsah rovný obsahu zadaného souboru. 2) Úsek paměti vyhrazený podprogramem ICUGETV (nebo příkazem LOAD) zaniká při vyvolání podprogramu ICUFREE včetně svého obsahu; stejně je tomu i u úseku paměti vyhrazeného podprogramem ICVIRG při dispozici 'READ' (zde je ovšem třeba volat podprogram ICVIRF). Naproti tomu úsek paměti vyhrazený podprogramem ICVIRG při dispozici 'WRITE', 'APPEND' nebo 'UPDATE' sice též při vyvolání podprogramu ICVIRF zaniká, avšak ještě předtím se její obsah zapíše do souboru a zůstává tam uložen trvale i po ukončení výpočtu.