Ročníkový projekt DYNAMICKÉ HTML Programátorská

Transkript

Ročníkový projekt DYNAMICKÉ HTML Programátorská
Ročníkový projekt
DYNAMICKÉ HTML
Programátorská dokumentace
Jan Ehrlich, Petr Marek, Tomáš Marván, Martin Paľo
Vedoucí projektu: RNDr. Vladimír Kuthan
1
Obsah
1.
Hlavní běh programu ................................................................................................................5
1.1.
2.
3.
Činnosti při startu .....................................................................................................6
1.1.1.
Načtení parametrů .......................................................................................6
1.1.2.
Definování a otevření souboru ......................................................................6
1.1.3.
Konfigurační soubor .....................................................................................7
1.2.
Jádro .........................................................................................................................8
1.3.
Činnosti při ukončení běhu ........................................................................................9
Parsery .................................................................................................................................... 10
2.1.
Obecný parser .......................................................................................................... 11
2.2.
Tag MISQL .............................................................................................................. 12
2.2.1.
Zpracování hlavičky tagu ............................................................................ 12
2.2.2.
Zpracování parametrů naparsovaných v hlavičce ....................................... 12
2.2.3.
Zpracování těla MISQL tagu ...................................................................... 12
2.3.
Tag MIBLOCK ........................................................................................................ 14
2.4.
Parsování tagu miblock ........................................................................................... 15
2.5.
Výkonná část tagu miblock ..................................................................................... 17
2.6.
Parser tagu MIVAR ................................................................................................. 18
2.6.1.
Úvodní nastavení a inicializace ................................................................... 18
2.6.2.
Parsování hlavičky tagu, atributů .............................................................. 18
2.6.3.
Zpracování chyb v hlavičce ......................................................................... 19
2.6.4.
Parsování těla tagu ..................................................................................... 19
2.6.5.
Vyvolání chybové obsluhy (hendleru) ......................................................... 20
2.6.6.
Předávání chybových pozic ......................................................................... 20
Zpracování tagu MIERROR ................................................................................................... 21
3.1.
Parsování tagu MIERROR ...................................................................................... 22
3.2.
Vykonání tagu MIERROR ...................................................................................... 23
3.3.
Parser tagu MIINCLUDE ....................................................................................... 24
3.4.
3.3.1.
Parsování tagu MIINCLUDE ..................................................................... 24
3.3.2.
Vykonání tagu MIINCLUDE ..................................................................... 24
Parser tagu MIINFO ............................................................................................... 25
3.4.1.
4.
5.
Zpracování těla tagu MIINFO .................................................................... 25
Zpracování tagů CHECKBOXLIST, RADIOLIST, SELECTLIST ......................................... 26
4.1.
Parsování tagů CHECKBOXLIST, RADIOLIST, SELECTLIST ........................... 27
4.2.
Provedení tagu CHECKBOXLIST, RADIOLIST, SELECTLIST ........................... 28
Proměnné ................................................................................................................................ 29
5.1.
Uložení v paměti ..................................................................................................... 30
5.1.1.
Celková koncepce ........................................................................................ 30
5.1.2.
Uložení hodnot podle SQL typu ................................................................. 32
5.1.3.
Strom indexů proměnné ............................................................................. 33
5.1.4.
Uložení jmen proměnných .......................................................................... 35
5.1.5.
Přidávání prvku do stromu Tpromtree ....................................................... 41
2
5.1.6.
5.2.
6.
7.
8.
Funkce pro práci s proměnnými .............................................................................. 45
5.2.1.
Funkce pro vkládání hodnot ....................................................................... 45
5.2.2.
Funkce pro získávání hodnot ...................................................................... 45
5.2.3.
Informační funkce ....................................................................................... 47
5.2.4.
Funkce dealokování ..................................................................................... 47
5.2.5.
Parsování proměnné ................................................................................... 48
Funkce ..................................................................................................................................... 50
6.0.1.
Úvodní nastavení a inicializace ................................................................... 50
6.0.2.
Parsování názvu funkce .............................................................................. 50
6.0.3.
Zpracování parametrů funkce ..................................................................... 50
6.0.4.
Zpracování požadavku funkce ..................................................................... 51
Aritmetika ............................................................................................................................... 53
7.1.
Desetinná čísla ......................................................................................................... 54
7.2.
Desetinná čísla ......................................................................................................... 55
7.2.1.
Uložení a popis čísel ................................................................................... 55
7.2.2.
Funkce pro inicializaci, uvolnění a vkládání hodnot ................................... 55
7.2.3.
Aritmetické funkce ...................................................................................... 56
7.2.4.
Funkce pro parsování čísla .......................................................................... 56
7.2.5.
Konverzní funkce ........................................................................................ 56
7.3.
Aritmetický výraz .................................................................................................... 58
7.4.
Vyhodnocování ........................................................................................................ 59
7.5.
Parsování bez vyhodnocování .................................................................................. 60
Bufferování vstupu .................................................................................................................. 61
8.1.
9.
Odebírání prvku ze stromu Tpromtree ....................................................... 42
Struktura systému vstupního bufferování ................................................................ 62
8.1.1.
Správce vložených souborů ......................................................................... 62
8.1.2.
Správce uložených stránek souborů ............................................................ 63
8.1.3.
Strom vložených souborů a řetězců ............................................................ 66
8.1.4.
Vkládání nových souborů a řetězců ............................................................ 67
8.1.5.
Funkce igetc, návratová pozice ................................................................... 68
8.1.6.
Init struktur a dealokace ............................................................................ 69
Zpracování chyb ...................................................................................................................... 70
9.1.
Obsloužení chyb ....................................................................................................... 71
9.2.
Generování chybových zpráv ................................................................................... 72
9.3.
Jazyková podpora .................................................................................................... 73
9.4.
Chybové obsluhy ..................................................................................................... 74
9.4.1.
Problematika .............................................................................................. 74
9.4.2.
Struktura a uložení ..................................................................................... 74
9.4.3.
Spuštění ...................................................................................................... 75
10.
Databáze ............................................................................................................................... 77
11.
Dynamické řetězce ................................................................................................................. 78
11.1. Struktura ............................................................................................................... 79
3
11.2.
Algoritmus přidávání řetězce ................................................................................. 80
11.3.
Funkce pro práci s dynamickými řetězci ................................................................ 81
4
1. Hlavní běh programu
5
1.1. Činnosti při startu
Činnosti při startu programu jsou odlišné pro každý použitý wrapper, tj. je odlišná pro CGI program,
pro modul do Apache 1.3 a Apache 2.0. Jejich odlišnost spočívá především v použitých funkcích, které
jsou na této rovině natolik odlišné, že pro implementaci bylo zvolena odlišná implementace činností při
startu.
Přesto mají tyto wrappery hodně společného, a to je použitý postup při startu. Ten se dá rozdělit
do následujících kroků:
- načtení parametrů z proměnných předaných v URL
- definování souboru, který bude použit pro daný dotaz a jeho otevření
- načtení konfiguračního souboru
Tyto kroky popíšu nyní podrobněji, a to na příkladě CGI programu. V ostatních wrapperech jsou
tyto kroky obdobné, pouze pro přístup k některým datovým položkám jsou použity API funkce příslušné
dané architektuře.
1.1.1. Načtení parametrů
V prvním kroku, poté co se pošlou HTTP hlavičky, se naparsují parametry předané v URL adrese. K
tomu se použije proměnná QUERY_STRING, ve které je uložen požadovaný řetězec. Ten se podle znaku &
nejprve rozdělí na jednotlivé proměnné a poté podle znaku = se oddělí název proměnné od jejího obsahu.
Poté se hodnota parametru převede pomocí funkce unurl() z řetězce, ve kterém mohou být zástupné
znaky pro hodnoty z horní poloviny ASCII tabulky na plnohodnotný osmibitový řetězec. Poté je daná
hodnota uložena na první volný index požadované proměnné. To má tu výhodu, že takto lze uložit více
hodnot do stejné proměnné, například pokud data přišla z formuláře, který využívá přepínač MULTIPLE.
1.1.2. Definování a otevření souboru
Jméno zpracovávaného souboru lze zadat dvěma způsoby, a to pomocí parametru MIval a přímo
uvedením cesty. První způsob je shodný se způsobem používaným v Informixovském modulu a funguje
vždy, navíc je způsobem preferovaným. Druhý způsob, kdy se zpracovává přímo strana požadovaná v
adrese má tu nevýhodu, že server Apache při využití formulářů nezpřístupňuje proměnnou potřebnou
pro toto vyhodnocení. Na druhou stranu nespornou výhodou je přehlednější výsledná URL stánky, což
lze především využít u statických stránek. Vlastní definování potom probíhá podobně, a to že nejprve
se zjistí zda je definována proměnná MIval. Pokud je, tak se pro určení souboru použije ta. Pokud není,
tak je použita proměnná PATH_INFO, ve které je uložena potřebná cesta. Před tuto cestu se navíc přidává
obsah makra IFMXHOME, ve kterém je definován adresář, ve kterém jsou uloženy skripty.
Když je proměnná určující jméno souboru definována, je soubor pomocí funkce vstup_buf_init()
otevřen. Ta se pokusí soubor otevřít a naplnit první buffer. V případě že se nepodaří uvedený soubor
otevřít, je vrácena chyba a program skončí.
6
1.1.3. Konfigurační soubor
Pro globální nastavení proměnných, které jsou často využívány, byl zaveden konfigurační soubor.
Vzhledem k pozdějšímu zavedení tagu MIINCLUDE, který tento konfigurační soubor může jednoduše a
plnohodnotně nahradit, je možné, že tento konfigurační soubor nebude v dalších verzích podporován.
V tomto konfiguračním souboru, jehož jméno je standardně prom.conf a je umístěn v koření adresáře
obsahujícího skripty, je následující: Od začátku řádky až po první znak = je název proměnné. Od tohoto
znaku dále až do konce řádky je potom hodnota proměnné. Řádka se nevyhodnotí, pokud je první znak
na řádce #. Zpracování tohoto souboru potom probíhá jednoduchým stavovým automatem.
7
1.2. Jádro
Hlavní jádro programu spočívá v zavolání obecného parseru. Ten je zavolán s parametry, které
znamenají, že je volán v nejnižší úrovni a jeho výstup jde rovnou na standardní výstup.
Po skončení obecného parseru je zavolána funkce flush_output_buffer(), která zajistí aby byly
zbytky nebufferovaného výstupu poslány na standardní výstup.
8
1.3. Činnosti při ukončení běhu
Po proběhnutí hlavního průchodu obecného parseru jsou odalokovány zdroje, které byly v průběhu
inicializace a běhu programu nashromážděny.
Nejprve je zrušen vstupní soubor, který byl použit. Poté jsou odalokovány defaultní proměnné.
Následuje odalokování struktur, která byly potřeba pro strom proměnných, jedná se o funkce dealoktree
a dealok_Tmierror_Koren.
V poslední fázi je potom ukončeno případné spojení s databází. Toto spojení zůstává po celou dobu
programu, aby se nemuselo kvůli každému SQL dotazu vytvářet nové, což by celou dobu běhu výrazně
prodloužilo.
9
2. Parsery
10
2.1. Obecný parser
Po inicializaci proměnných je zavolán obecný parser, který parsuje soubor, pokud se aktuální pozice
v souboru nenachází zrovna uvnitř tagu MIVAR.
Tento parser je implementován ve funkci parse v souboru mimain/parse.c.
Kromě zavolání na začátku zpracování je tato funkce volána i pro každé parsování těla příkazu
MIBLOCK.
Základem pro tento obecný parser je stavový automat, který na základě právě načteného znaku
mění stav automatu. Tento automat končí svoji činnost, pokud nastane konec MIBLOCKu, je-li tento
parsován, nebo na konci vstupního souboru. Základním stavem je stav 0, který značí že parser nezpracovává žádný tag. Pokud parser v tomto stavu nalezne na vstupu znak ¡, přesouvá se do stavu 1, a
začíná rozpoznávat jednotlivé tagy. Celý tento automat je realizován pomocí jednoho rozsáhlého while
cyklu. Zde je na začátku ošetřeno jedním velkým switchem rozpoznávání jednotlivých stavů. Automat
se posune do dalšího stavu, pokud v daném stavu nalezne odpovídající písmeno. Jinak cyklus pokračuje,
a to ošetřením stavů, ve kterých je rozpoznáno celé klíčové slovo. V tomto případě je zavolána příslušná
parsovací funkce pro dané klíčové slovo, která dané slovo buď zpracuje nebo přeskočí.
Na konci celého cyklu je potom vytištěn znak, pokud se automat nachází v základním stavu. Pokud
se nachází ve stavu, kdy nerozpoznal klíčové slovo, které začal úspěšně rozpoznávat, tak ohlásí chybu a
připojí klíčové slovo na základě znalosti stavu pomocí funkce main_status2stav().
Po proběhnutí celého cyklu je nakonec ošetřen stav, kdy byl nalezen konec souboru uprostřed MIBLOCKu, a je vrácena návratová hodnota označující způsob, jakým funkce parse skončila.
11
2.2. Tag MISQL
Zpracování MISQL tagu se dá rozdělit na následující čtyři hlavní části:
- Zpracování hlavičky tagu
- Zpracování parametrů naparsovaných v hlavičce
- Zpracování těla MISQL tagu
Kód zpracování MISQL tagu je ve funkci misql() v souboru misql/misql.c.
2.2.1. Zpracování hlavičky tagu
Jako první probíhá zpracování hlavičky tagu. V ní se pomocí stavového automatu podobně jako v
obecném parseru rozpoznávají jednotlivé parametry tagu MISQL. V případě, že je rozpoznán parametr,
tak se zavolá jedna z funkcí parsesql() pro textovou hodnotu nebo parsenum() pro číselnou hodnotu.
Tyto funkce naparsují a vyhodnotí hodnoty daných parametrů, které mohou obsahovat i proměnné.
Tak se pozice v souboru přesune za daný parametr a začíná rozpoznávat další, dokud nenalezne znak ¡,
znamenající konec tagu.
Chyby vzniklé během zpracování hlavičky nejsou ihned vypisovány na výstup, ale jsou ukládány
do pomocné proměnné. V momentě, kdy je zpracována celá hlavička, se při neexistenci atributu ERR
pomocná proměnná vypíše, v opačném případě, pokud je obslužný handler definován, je zavolán ten.
Tímto způsobem se zamezí výskytu situace, kdy pro chybu v parametru není zavolán handler, protože
je definován až v pozdějším atributu.
2.2.2. Zpracování parametrů naparsovaných v hlavičce
V dalším kroku se provede SQL dotaz, pokud se nejedná o SELECT. V tomto případě se vykoná
pouze dotaz a tělo bloku se přeskočí.
V následujícím kroku se vytvoří proměnná obsahující jméno proměnné, ve které budou k dispozici
výsledky SQL dotazu. Do té je buď zkopírována proměnná s parametrem RESULT, pokud je tento
parametr definován, v opačném případě je vytvořena proměnná s defaultním jménem. Poté se pomocí
funkce ȯbsazeno(). zkontroluje, zda už je dané jméno použito. Pokud není, tak se pomocí funkce ȯbsad().
obsadí, v opačném případě je nahlášena chyba.
V následujícím kroku se uloží proměnné ovlivňující výsledek dotazu ( zajištěno parametry WINSIZE,
DATASET a DEFAULT), Následuje příprava SQL dotazu a případné posunutí ve výsledku, jeli použit
parametr WINSTART.
Poté se pouze uloží nastavení pomocných proměnných, a zpracování pokračuje dalším krokem.
2.2.3. Zpracování těla MISQL tagu
Celé zpracování probíhá ve dvou navzájem vnořených cyklech. Ve vnějším se prochází přes všechny
řádky SQL dotazu, ve vnitřním se potom pomocí stavového automatu prochází tělo MISQL bloku a
vyhodnocuje se. Toto Vyhodnocení z větší části kopíruje obecný parser(viz obecný parser), pouze se
12
podstatně liší v jedné věci, a tou je výskyt proměnných v těle. V případě, že se v těle bloku vyskytne
znak $, tak se podle následujícího znaku vloží buď obsah proměnné nebo funkce.
Na konci vnějšího cyklu se potom ukládají výsledky vyhodnocení do proměnných (pokud byl definován parametr NAME). Po konci obou cyklů se v závěrečném bloku uvolní všechny upotřebené zdroje,
a tím zpracování konči.
13
2.3. Tag MIBLOCK
Zpracování tagu miblock zabezpečuje funkce int miblock(TParsPozice pozice, int uroven, Tdata dur
*vystup). Zpracování tagu je rozděleno do dvou částí. V první části se tento tag naparsuje, přičemž
jsou vráceny naparsované hodnoty. V druhé části proběhne vlastní vykonání tagu s použitím hodnot
získaných v první části. Tato funkce jako svojí návratovou hodnotu vrací pozici za zpracovávaným tagem
miblock.
14
2.4. Parsování tagu miblock
Pro parsování tagu se používá funkce int miblockpars(TParsPozice pozice, int *Flag, int *indexy,
IINDEX *INDEX, char *foreach, int vyhod, Tdata dur *vystup). Při parsování se užívá pole par[], které
obsahuje názvy všech platných atributů, které se mohou v tagu miblock vyskytovat, parametr Flag slouží
pro identifikaci atributu, který je zrovna zpracováván. Do pole indexy[] se postupně ukládají hodnoty
jednotlivých atributů pro miblock typu cyklus - atributy from, to, step, resp. číselné hodnoty resp.
hodnoty proměnných uvedených za atributy cond a while, případně jsou-li za posledně zmiňovanými
atributy uvedeny jako hodnoty funkce, jsou do tohoto pole uloženy pozice těchto funkcí.
Název indexové proměnné včetně jejího případného indexu, pokud se jedná o položku pole, je uložen
do proměnné INDEX. Pokud je v tagu nalezen atribut foreach, jeho hodnota( proměnná, přes kterou
se má provádět cyklus ), se uloží do proměnné foreach. Atribut vyhod udává, zda se má při parsování
funkci resp. proměnných volat jejich vyhodnocování, nebo zda se mají přeskakovat. Je-li atribut vystup
nenulový, je sem přesměrován veškerý výstup. Pro správné vypisování názvu případných nepovinných
atributů, které se mohou v tagu vyskytovat, slouží proměnná buf. Při přečtení každého dalšího znaku se
do ní tento znak připíše. Nastane-li situace, kdy se použije pro přeskočení nepovinného atributu funkce
preskoc(), je jí tato proměnná předána jako parametr.
Pro možnost vypisování upozornění pro vícenásobný výskyt atributů slouží proměnné I pro index,
T pro to, F pro from, S pro step, W pro while, R pro foreach, CC pro cond. V momentě, kdy je
naparsován některý z platných atributů, nastaví se příslušná proměnná na hodnotu 1, přičemž, když už
tato proměnná měla hodnotu 1, vypíše se upozornění.
Vlastní parsování se skládá ze dvou částí. V jedné části se nejdříve podle aktuálně čteného znaku
ze vstupu určí, o jaký atribut se pravděpodobně jedná - Flag. V dalším zpracování se porovnávají
čtené znaky se znaky určeného atributu. V případě nerovnosti je čtený atribut pokládán za nepovinný,
volá se funkce preskoc(), která zajistí přeskočení celého( resp. i více za sebou ) nepovinného atributu,
přičemž se vypíše chybové hlášení o tomto atributu. Tato funkce dostává jako svůj parametr pozici, na
které leží přeskakovaný atribut. Také je jí předáván atribut buf, ve kterém je uložena již přečtená část
přeskakovaného atributu. Funkce preskoc() může být volána jak z tagu miblock, tak z tagu mielse. V
případě volání z tagu mielse, v ní proběhne také zpracování případného platného atributu cond. Jeho
hodnota je vyhodnocena a vrácena v podobě atributu cond1. Dalším parametrem předávaným této funkci
je vyhod, který určuje, zda-li se mají při zpracování hodnot proměnných resp. funkcí tyto vyhodnocovat.
Toho se využívá při přeskakování celého tagu miblock.
V druhé části se pak, když se povede přečíst celý atribut, zapamatuje hodnota daného atributu - to
v případě, že se jedná o proměnnou nebo číselnou hodnotu. V případě, že se jedná o atributy cyklu from,
to, step, jsou tyto hodnoty uloženy na odpovídající místa do pole indexy[]. V případě, že jako hodnota
atributu vystupuje funkce, zapamatuje se její pozice, aby pak bylo možno tuto funkci ve výkonné funkci
tagu miblock vyvolat. Když se vyskytne u atributu cond číslo nebo proměnná, je do položky indexy[3]
uložena hodnota 0 resp. 1, podle toho, zda je tato hodnota nulová či nikoli. V případě, že se za atributem
cond vyskytuje funkce, je do položky indexy[3] uložena pozice této funkce. Obdobně je to s atributem
while, pro který se hodnoty čísla, proměnné resp. umístění funkce ukládají do položky indexy[4]. V
případě, že je jako hodnota uvedena funkce eval, která vkládá svůj výsledek za svoje umístění, je tento
výsledek zpracováván jakoby se jednalo o původní hodnotu atributu.
15
V tagu miblock může být navíc uveden atribut err. Tento atribut je parsován podobným způsobem
jako již uvedené atributy. Po získaní hodnoty tohoto atributu, pomocí funkcí pridej char Tdata dur()
a get retezec Tdata dur(), je ověřeno, zda uvedený hendler skutečně existuje. Pokud ano, je nastavena
globální proměnná MI ERROR ACTUAL na tento handler.
Během parsování je veškerý výstup přesměrován do Tdata duru *mujdur. Po skončení parsování se
hodnota tohoto Tdata duru, v případě, že parametr vystup je definován, přesune do proměnné vystup,
která byla předaná parsovací funkci jako parametr. Není-li tato proměnná definována, je obsah proměnné
mujdur vypsán na standardní výstup. V případě, že během parsování došlo k nějaké chybě, je po skončení
parsování volaná funkce mierror slupka().
Ke konci parsovací funkce se určí, jaký je skutečný typ naparsovaného tagu miblock - tedy zda se
jedná o miblock s podmínkou, cyklem while, cyklem from to step, nebo foreach. Dle tohoto typu pak
proběhne vlastní vykonání zpracovávaného tagu miblock.
16
2.5. Výkonná část tagu miblock
Výkonnou část tagu miblock zabezpečuje funkce miblockrob(), které jsou předány jako parametry
hodnoty získané pomocí parsovacích funkcí a pozice, od které má být dále prováděno vlastní tělo tagu
miblock. Když se v tagu miblock vyskytují všechny atributy jednoho typu tagu miblock( from, to, step,
while, foreach ) a navíc je přítomen i atribut cond, je tělo miblocku prováděno jen za předpokladu splnění
této podmínky. Když není atribut cond v takovém případě uveden, je považován za splněný. Pak se podle
typu zpracovávaného tagu miblock provede jedna část výkonné funkce.
Tzn., vyskytuje-li se v miblocku atribut cond, u kterého je jako hodnota uvedena funkce, je na
základě její zapamatované pozice voláno její vyhodnocení a následné provedení těla miblocku závisí od
hodnoty, kterou tato funkce vrátí. V případě, že se v miblocku vyskytuje atribut cond, u kterého je jako
hodnota uvedena proměnná, jejíž hodnota je jíž známá z parsovací funkce, tělo miblocku se provede,
pokud je tato hodnota nenulová.
Vyskytují-li se v miblocku atributy index, from, to, případně step, provádí se vykonání cyklu s
mezními hodnotami získanými v parsovací funkci. Při každém průběhu cyklem se přitom nastavuje
správná hodnota indexové proměnné - pomocí volaní funkce setprom ar().
V případě, že se v miblocku vyskytuje tag foreach, jsou v poli indexy[] uloženy index první položky
v poli uvedeném jako hodnota atributu foreach a počet všech položek. Pro každou položku tohoto pole se
pak provede cyklus, přičemž hodnota následující položky se získává voláním funkce getprom next id().
Jedná-li se o miblock typu while s funkcí, je před každým dalším během cyklu volána funkce mifce(),
která vrací aktuální hodnotu funkce. To znamená, že cyklus běží až do chvíle, kdy funkce mifce() vrátí
nulovou hodnotu.
Obdobně je tomu, jedná-li se o miblock typu while s proměnnou. V tomto případě se ale kontroluje
hodnota proměnné uvedené za atributem while. Cyklus běží dokud je hodnota nenulová.
Při vykonávání každého z uvedených typu tagu miblock se volá funkce parse(). Po jejím volání
se kontroluje její atribut udávající, zda-li provádění těla tagu miblock skončilo na ukončovacím tagu
miblock nebo na tagu mielse. V 1. případě, když se nejedná o cyklus, je vykonávání tagu miblock
ukončeno, pokud jde o cyklus, je prováděna další iterace. Když se ale narazí na tag mielse, je volána
funkce dokonci miblock(), která zpracuje všechny případné tagy mielse. Pomoci funkce preskoc() přeskočí
případné nepovinné atributy uvedeny v tazích mielse, resp. platný atribut cond, na základě kterého vyvolá
buď provedení nebo přeskočení příslušné části těla miblocku odpovídající právě zpracovávanému mielse.
17
2.6. Parser tagu MIVAR
Parser tagu MIVAR je implementován ve funkci
TParsPozice mivar(TParsPozice pozice, Tdata_dur * stdven)
Průběh parsování lze rozdělit na tyto po sobě jdoucí části:
1. Úvodní nastavení a inicializace
2. Parsování hlavičky tagu, atributů
3. Zpracování chyb v hlavičce
4. Parsování těla tagu
5. Vyvolání chybové obsluhy (hendleru)
Pozn.: Deklarace funkce je v hlavičkovém souboru mivar/promenne.h, definice těla funkce společně
s pomocnými funkcemi a makry je v souboru mivar/mivar parser.c
2.6.1. Úvodní nastavení a inicializace
Funkce mivar potřebuje parametr pozice ve vstupním proudu, kde má začít parsovat a dynamický
řetězec pro výpis chyb. Je-li nulový jsou chyby posílány na výstup. Rodičovská parsovací funkce zpracuje
úvodní řetězec tagu: ”<?MIVAR”. Pozice předaná v parametru odkazuje na první znak za tímto řetězcem.
Kromě parametrů funkce obdrží i odkaz na uloženou pozici prvního znaku tagu, tedy znaku M v
řetězci ”<?MIVAR”. Hodnota pozice je uložena v globální proměnné michyba_pozice, a je nastavovaná
rodičovskou funkcí. Po jejím uložení do lokální proměnné ch_vstup je proměnná michyba_pozice vynulována.
Uložená pozice prvního znaku tagu je využívána při hlášení některých chyb (např. předčasný konec
souboru), kde se vypíše spolu s chybou jako pozice úvodního párového tagu, ve kterém došlo k chybě.
Více informací o ukládání chybové pozice pomocí proměnné michyba_pozice viz. kapitola o zpracování
chyb.
V parametru stdven je odkaz na dynamický řetězec pro vypisování chyb. Ten se vkládá do volání
funkce chybového výpisu michyba(). U vážných chyb se ale nepředává, protože ty jej stejně nepoužívají
a vypisují přímo na výstup.
Pro zpracování hlavičky je jeho hodnota zálohována v řetězce zaloha_stdven. Navrácena je po
doparsování hlavičky tagu.
2.6.2. Parsování hlavičky tagu, atributů
V hlavičce se parsují následující atributy, jsou uvedeny spolu s proměnnými indikujícími, zda již
byly parsovány:
- atribut NAME indikuje name,
- atribut DEFAULT indikuje mivar_def.hodnota,
- atribut COND indikuje coex,
- atribut ERR indikuje err.
Proměnná name obsahuje řetězec s názvem proměnné, do které se přesměruje výstup těla. V proměnné mivar_def je uložena defaultní hodnota pro obsah nedefinovaných proměnných. coex rovno
18
jedné indikuje naparsování tagu COND a err rovno jedné indikuje atribut ERR. Jméno chybové obsluhy(hendleru) je uloženo během parsování atributu ERR do globální proměnné MI_ERROR_ACTUAL, je-li
ovšem chybová obsluha s tímto jménem definována.
Při naparsování neznámého tagu je zavolána parsovací funkce mivar_parser_spolkni_atribut()
Jedním z jejích parametrů je řetězec se jménem parsovaného neznámého tagu pro chybový výstup. Funkce
vypíše chybovou hlášku spolu s názvem neznámého tagu.
Parsování hlavičky končí přečtením znaku >, nebo fatální chybou.
2.6.3. Zpracování chyb v hlavičce
V hlavičce jsou všechny chyby přesměrovány do dynamického řetězce hlav_error z důvodu, že by
mohl být atributem ERR definován nový obsluhující chybový hendler a žádná chyba tedy nesmí být
vypsána.
Došlo-li k chybě, obsahuje proměnná nastala_chyba číslo první chyby obsloužené funkcí michyba(),
jinak je proměnná rovna nule. Další možné chyby již tuto hodnotu nemění. Výpisy chyb byly přesměrovány do dynamického řetězce hlav_error, pokud došlo k chybě v hlavičce a není definována žádná
uživatelská chybová obsluha (hendler) je obsah tohoto řetězce poslán do řetězce stdven (druhý parametr
funkce), nebo na výstup, je-li nulový.
Pokud došlo k chybě a je-li definován chybový hendler (Globální proměnná určující název obsluhujícího hendleru MI_ERROR_ACTUAL je nenulová, nebo je nenulová globální proměnná meh_generic obsahující definici generického hendleru.), je nastavena proměnná callni_hendler na jedničku a nastaveno
přeskočení těla tagu na konec, kde se zavolá uživatelská chybová obsluha.
V tagu MIVAR se uživatelské chybové obsluhy spouštějí na konci tagu.
2.6.4. Parsování těla tagu
Výsledek parsování těla jde standardně na výstup nebo do dynamického řetězce stdven, v případě,
že se však jedná o tag MIVAR s definovaným atributem NAME, je výstup veden do řetězce outp. V
závěru tagu je přiřazena jeho hodnota do proměnné s názvem v name.
Výstup se bufferuje do pole parse_output_buffer o maximální délce PARSE_OUTPUT_BUFFER_LEN
znaků.
Pro posílání řetězců a znaků na výstup existují makra, která v sobě zajišťují přesměrování výstupu
do dynamického řetězce (podle výše uvedeného pravidla) nebo bufferování a následné vypisování pomocí
funkce iwrite() Jsou to:
mivar_parser_vyputni_char(zn)
mivar_parser_vyputni_ret(retezec, delka)
V těle tagu se vyhodnocují proměnné (volání mivar_parse1prom() spolu s getprom_ar()) a uživatelské funkce (volání mifce()). Kromě koncového párového tagu <?/MIVAR> se rozpoznává tag pro
komentář <?MIINFO>, ten je parsován funkcí miinfo().
Chyby jsou posílány na výstup. Je kontrolována hodnota globální proměnné nastala_chyba. Oproti
hlavičce je zde její chování pozměněno přepínačem nastala_chyba_detekovat rovným nule, aby indikovala první chybu a to pouze, je-li definována nějaká uživatelská chybová obsluha. V případě, že
19
nastala_chyba obsahuje nenulové číslo, je přerušeno vyhodnocování těla. Zbytek těla parsuje bez vyhodnocování funkce skip_mivar().
2.6.5. Vyvolání chybové obsluhy (hendleru)
Pokud v hlavičce došlo k chybě, a byla-li zároveň definována některá chybová obsluha, je nastavena
hodnota callni_hendler na jedničku.
Pokud v těle došlo k chybě, při níž byl definován chybový obslužný hendler, obsahuje nastala_chyba
jeho číslo.
V obou případech se volá vykonání chybové obsluhy mierror_slupka(), předává se jí pozice za
posledním znakem párového uzavíracího tagu <?/MIVAR> spolu s řetězcem stdven pro chybový výstup.
2.6.6. Předávání chybových pozic
Pro množství různých chyb, které mohou vzniknout v tagu MIVAR, je třeba ohlásit pozici na
jiném místě, než chyba vznikla. Například pro chybu předčasného konce souboru je třeba předat funkci
michyba() prostřednictvím globální proměnné michyba_pozice pozici začátku tagu. Tedy tagu, který
není ukončen a způsobil chybu, třebaže chyba byla zjištěna až na konci souboru.
Funkcím mifce(), miinfo() je třeba také předat pozici prvního znaku.
Pro tyto účely jsou alokována dvě lokální pole: ch_bufp a ch_bufp2. Může dojít k situaci, kdy je
najednou potřeba uložit pozice dvě. Např. v uvozovkách kolem hodnoty atributu COND se vyskytuje
funkce. Je třeba si pozici pamatovat pro první uvozovku, a dále pozici začátku funkce, která se předá
funkci mifce().
20
3. Zpracování tagu MIERROR
Zpracování tagu mierror má dvě části. V první části se tento tag parsuje, přičemž se z něj získávají
hodnoty platných atributů. Tyto atributy představuje pole atr[], do kterého se postupně ukládají pozice
hodnot jednotlivých atributů - err, cond, tag, SQL. Ty jsou pak předány druhé části, která tyto hodnoty
zpracovává.
21
3.1. Parsování tagu MIERROR
Při parsování se užívá pole err[], které obsahuje všechny platné atributy, které se v tomto tagu mohou
vyskytovat. Proměnná Flag slouží k identifikace atributu, který se zrovna zpracovává. Každý přečtený
znak se porovnává s příslušnou hodnotou pole err[]. Pokud dojde k nerovnosti obou znaků, je vypsáno
chybové hlášení.
K zpracování podmínkového atributu cond, slouží funkce zpracuj cond(), která vrátí hodnotu tohoto
atributu. K získaní hodnot ostatních atributů slouží funkce zpracuj flag().
22
3.2. Vykonání tagu MIERROR
Do lokálních proměnných se uloží prostřednictvím funkce text() hodnoty jednotlivých atributů.
Následně je načteno tělo atributu pomocí funkce pridej char Tdata dur() a jeho hodnota uložena do
lokální proměnné pomocí funkce get retezec Tdata dur(). S takto získanými hodnotami je volaná funkce
mierror set(), která vloží tagem definovaný chybový handler do interní datové struktury.
23
3.3. Parser tagu MIINCLUDE
Tag miinclude obsahuje jediný atribut, který se nejdříve naparsuje a je získaná jeho hodnota a pak
se provede vlastní vložení definovaného souboru.
3.3.1. Parsování tagu MIINCLUDE
Při nalezení začátečního znaku jména atributu name a dalším postupném čtení, se kontroluje, zda je
jméno atributu uvedeno správně. Znaky čtené ze vstupního souboru se porovnávají s jim odpovídajícími
znaky uvedenými v poli inc[]. V případe neshody je vypsáno chybové hlášení. U vlastní hodnoty atributu
je zapamatována její pozice ve vstupním souboru.
3.3.2. Vykonání tagu MIINCLUDE
Za pomoci pozice hodnoty atributu name získané při parsování tagu je tato hodnota načtena. Následně je volaná funkce vloz filestring(), která provede vlastní vložení zadaného souboru do vstupního
proudu.
24
3.4. Parser tagu MIINFO
Parser tagu MIINFO je implementován ve funkci
int miinfo(Tdata_dur *warning, TParsPozice *pozice);
Tag MIINFO nemá žádné atributy a obsah tagu se nevyhodnocuje, proto je parser velmi jednoduchý.
Pozn.:
Deklarace funkce je v hlavičkovém souboru mifce/fce.h, definice těla funkce společně s pomocnými
funkcemi je v souboru mifce/fce.c
3.4.1. Zpracování těla tagu MIINFO
Funkce miinfo potřebuje parametr pozice ve vstupním proudu, kde má začít parsovat a dynamický
řetězec pro výpis chyb. Je-li nulový jsou chyby posílány na výstup. Rodičovská parsovací funkce zpracuje
počáteční tag: ”<?MIINFO>”. Pozice předaná v parametru odkazuje na první znak za tímto řetězcem.
Kromě parametrů funkce obdrží i odkaz na uloženou pozici prvního znaku tagu, tedy znaku M v
řetězci ”<?MIINFO>”. Hodnota pozice je uložena v globální proměnné michyba_pozice, a je nastavována
rodičovskou funkcí. Hodnotou je inicializována lokální proměnná chyba_pozice.
Uložená pozice prvního znaku tagu je využívána při hlášení některých chyb (např. předčasný konec
souboru), kde se vypíše spolu s chybou jako pozice úvodního párového tagu, ve kterém došlo k chybě.
Více informací o ukládání chybové pozice pomocí proměnné michyba_pozice viz. kapitola o zpracování chyb.
V parametru warning je odkaz na dynamický řetězec pro vypisování chyb. Ten se vkládá do volání
funkce chybového výpisu michyba().
U vážných chyb se ale nepředává, protože ty jej stejně nepoužívají a vypisují přímo na výstup.
Jak již bylo uvedeno, tělo MIINFO se nevyhodnocuje a je zcela ignorováno. Parser načítá jednotlivé
znaky a testuje vstup na řetězec koncového tagu <?/MIINFO>. Pokud parser za řetězcem <?/MIINFO
narazí na jiný, než prázdný znak nebo >, je generováno chybové hlášení a následně předána pozice na
tomto znaku rodičovské funkci. Parser v takovém případě předpokládá, že tag MIINFO na tomto znaku
končí (byla zapomenuta koncová závorka >).
25
4. Zpracování tagů CHECKBOXLIST, RADIOLIST, SELECTLIST
Zpracování tagu má dvě části. V první části se tagy parsují, v druhé jsou na základě v nich uvedených
hodnot vykonány.
26
4.1. Parsování tagů CHECKBOXLIST, RADIOLIST, SELECTLIST
Parsování tagů CHECKBOXLIST a RADIOLIST zabezpečuje stejná funkce radiocheckboxlistpars().
Názvy jednotlivých atributů tagů jsou postupně čteny ze vstupního souboru a syntakticky porovnávány
s položkami pole check[], ve kterém jsou uvedeny správné jména všech atributů. V případě neshody
čtených znaků se znaky v poli check[] je vypsáno chybové hlášení. Tyto tagy mohou obsahovat atributy,
jejichž jména mají stejný prefix - checked-checkone, pre-post. Proto se při čtení prvního rozdílného znaku
mezi oběma podobnými jmény, nastaví na příslušnou hodnotu ukazatel do pole check[], čímž se zabezpečí
správná kontrola celého jména atributu. Pomocí funkce zpracujflag() se uloží do pole předávaného jako
parametr parsovací funkce, pozice hodnot jednotlivých atributů. V případě nalezení nepovinného atributu
v tagu, je tento atribut přeskočen pomoci funkce natribut(), která vrátí pozici za tímto nepovinným
atributem.
Obdobným způsobem je parsován tag SELECTLIST.
27
4.2. Provedení tagu CHECKBOXLIST, RADIOLIST, SELECTLIST
Na základě hodnot předaných z parsovací funkce jsou získány hodnoty jednotlivých atributů tagů.
V případě, že je uveden atribut SQL, je výraz za nim uvedený parsovaný pomocí funkce parsesql().
Následně je volaná funkce napln SQL list rada(), která vytvoří řadu z hodnot získaných prostřednictvím uvedeného SQL dotazu, a uloží ji do proměnné. Název této proměnné je uveden v konstantě
SQL LIST ONCE PROM ARRAY. Tato řada se musí následně připravit pro zpracovávání prostřednictvím funkce priprav SQL list(). Z této řady se jednotlivé hodnoty postupně získávají voláním funkce
napln SQL list(), po jejímž zavolání je do proměnné, jejíž název je uložen v konstantě SQL LIST PROM,
uložena aktuální hodnota výsledku SQL dotazu. Na základě takto získaných hodnot se vytvoří tagy
CHECKBOXLIST, RADIOLIST, SELECTLIST. Po ukončení práce je nutno volat funkci zrus SQL list()
a saratice(), která smaže všechny položky proměnné užité pro uložení hodnot z SQL dotazu.
28
5. Proměnné
29
5.1. Uložení v paměti
5.1.1. Celková koncepce
Hodnoty proměnných, ke kterým má uživatel při běhu programu přístup, jsou uloženy ve stromu v
paměti. Ten lze rozdělit do tří vrstev. Ve směru odshora dolů to jsou:
1. uložení jmen proměnných,
2. uložení obsazených indexů proměnné,
3. konkrétní hodnoty uložené podle jejich SQL typu.
Vychází se z celkové koncepce, kde proměnná, na níž se odkazuje jménem, obsahuje soubor hodnot
prvků, na něž lze odkazovat celočíselným indexem. Jednotlivé položky proměnné pak představují cílové
hodnoty, uložené podle jejich původního SQL typu. Na uživatelskou proměnnou lze nahlížet jako na
pojmenované řídké pole. Každý prvek tohoto pole smí obsahovat jednu konkrétní hodnotu libovolného
podporovaného SQL typu.
30
Obrázek 1: Třívrstvý model stromu proměnných
31
Strom proměnných je v paměti jen jeden, uložen v globální proměnné Tpromtree * strom_prom.
5.1.2. Uložení hodnot podle SQL typu
Uložení jednotlivých proměnných v paměti vychází z jejich databázového typu. Hodnoty z databáze
se ukládají vesměs v typech podobných jejich struktuře. Není implementována celá paleta databázových
typů, ale jen důležitý výsek. A to takový, na nějž lze plnohodnotně převádět všechny hodnoty z databáze při jejich načítání. Například nebyl implementován typ SQLMONEY, protože jej plně nahradí typ
SQLDECIMAL.
Pro uchování hodnot z databáze slouží struktura TpromtypeSQL, v ní jsou typy rozlišovány podle
položky typ. Jeho položku data tvoří unie T_UN_promtypeSQL, ta obsahuje konkrétní hodnotu v závislosti
na typu.
Výčet vnitřních implementovaných typů s jejich číslem enumeruje:
typedef enum {SQLNULL = -1,
SQLCHAR = 0,
SQLSMINT = 1,
SQLINT = 2,
SQLDECIMAL = 5,
SQLSERIAL = 6,
SQLDATE = 7,
SQLDTIME = 10,
SQLVCHAR = 13,
SQLINTERVAL = 14
}sqlTypeID;
Jejich číslování se vesměs kryje s implementací Informixu.
Implementace typů obsažených v unii T_UN_promtypeSQL:
- SQLCHAR - řetězec pevné délky - implementuje 0 TpromtypeSQL_SQLCHAR
- SQLSMINT - malý integer - implementuje struktura TpromtypeSQL_SQLSMINT
- SQLINT - integer, celé číslo - implementuje struktura TpromtypeSQL_SQLINT
- SQLDECIMAL - desetinné číslo - implementuje struktura TpromtypeSQL_SQLDECIMAL Číslo je
uloženo v podobě typu UCislo viz. desetinná čísla.
- SQLSERIAL - serial - implementuje struktura TpromtypeSQL_SQLSERIAL
- SQLDATE - datum - implementuje struktura TpromtypeSQL_SQLDATE
- SQLDTIME - datum a čas s přesností na desetitisíciny sekundy - implementuje struktura TpromtypeSQL_SQLDTIME
- SQLVCHAR - řetězec volitelné délky - implementuje struktura TpromtypeSQL_SQLVCHAR.
- SQLINTERVAL - časový interval - implementuje struktura TpromtypeSQL_SQLINTERVAL
Všechny jednotlivé databázové typy jsou používány pouze pro data získaná z databáze. Pro hodnoty
proměnných, které si definuje uživatel sám za běhu aplikace je využíván jen typ SQLCHAR.
Pro databázovou hodnotu NULL, je definován zvláštní typ SQLNULL. Představuje-li TpromtypeSQL
tuto hodnotu, je její položka data rovna 0.
Pro jednotlivé typy jsou definovány konstruktory, které vytvoří vnitřní strukturu proměnné a naplní
defaultními hodnotami.
32
TpromtypeSQL * CreateProm_SQLNULL()
TpromtypeSQL * CreateProm_SQLSMINT()
TpromtypeSQL * CreateProm_SQLINT()
TpromtypeSQL * CreateProm_SQLDECIMAL(Tdata_dur * stdven)
TpromtypeSQL * CreateProm_SQLSERIAL()
TpromtypeSQL * CreateProm_SQLDATE()
TpromtypeSQL * CreateProm_SQLDTIME()
TpromtypeSQL * CreateProm_SQLINTERVAL()
TpromtypeSQL * CreateProm_SQLCHAR(int n)
TpromtypeSQL * CreateProm_SQLVCHAR(int n)
Hodnoty se do vytvořených proměnných ukládají pomocí funkcí:
int SetPromSQL_SQLNULL(TpromtypeSQL *)
int SetPromSQL_SQLSMINT(TpromtypeSQL *, const short int)
int SetPromSQL_SQLINT(TpromtypeSQL *, const long int)
int SetPromSQL_SQLDECIMAL(TpromtypeSQL *, const TCislo)
int SetPromSQL_SQLSERIAL(TpromtypeSQL *, const long int)
int SetPromSQL_SQLDATE(TpromtypeSQL *, const int year,
const char mon, const char day)
int SetPromSQL_SQLCHAR(TpromtypeSQL *, const char *,
const unsigned int)
int SetPromSQL_SQLVCHAR(TpromtypeSQL *, const char *,
const unsigned int len)
Existuje i kopírovací konstruktor
TpromtypeSQL * KopyProm_SQL(const TpromtypeSQL *)
Celý obsah SQL proměnné se ruší destruktorem
void DestroyProm_SQL(TpromtypeSQL *)
5.1.3. Strom indexů proměnné
Definované hodnoty na jednotlivých indexech proměnné jsou implementovány AVL-stromy. Každý
vrchol stromu představuje definovanou hodnotu. Z té je odkaz přímo na její hodnotu do struktury
TpromtypeSQL.
Vrcholy AVL stromu jsou typu Tpromenna. Struktura pro kořen stromu je TpromennaKoren. Obsahuje odkaz na první vrchol stromu, počet vložených prvků a nejvyšší vložený prvek. Tyto hodnoty se
aktualizují po každém vložení nebo odebrání prvku.
33
Obrázek 2: AVL strom indexů proměnné
34
Definovaný typ pro index je definován v makru Tpromindex a představuje hodnotu unsigned int.
Spolu s makrem je definována také nejvyšší povolená hodnota indexu MAX_TPROMINDEX. Nejnižší hodnota
indexu je 1.
Funkce pro pro vložení prvku x do stromu je
Tpromenna * vloz_TpromennaKoren(const Tpromindex x,
TpromennaKoren ** p)
Funkce pro pro odebrání prvku x ze stromu je
int delete_vrchol_TpromennaKoren(const Tpromindex x,
TpromennaKoren ** k)
U těchto dvou funkcí je předáván dvojný ukazatel na kořen stromu z následujícího důvodu: Pokud je
přidáván první prvek a kořen není na alokován, je vytvořen. Pokud je odebírán poslední prvek ze stromu,
je uvolněna i hlava a odkaz na ni je vynulován. Na prázdnou hlavu je nahlíženo jako na prázdný strom.
Hledaní prvku zajišťuje funkce
Tpromenna * hledej_TpromennaKoren(const Tpromindex x,
const TpromennaKoren * k)
Dealokaci celého stromu, včetně odkazů na hodnoty TpromtypeSQL provede funkce
void dealok_Tpromenna(Tpromenna ** p)
5.1.4. Uložení jmen proměnných
Jména proměnných jsou uložena ve stromu připomínajícím trie. Vychází se sice z původní definice
trií, ale provádí se úsporná opatření a optimalizace:
1. Strom se skládá z vrcholů TpromtreeSlov označme si je jako slovo-vrcholy, a z vrcholů typu
TpromtreePism ty si označme jako písmeno-vrcholy. Slovo-vrcholy mohou obsahovat libovolný
řetězec délky alespoň jedna, zatímco písmeno-vrcholy mohou obsahovat pouze jediný znak.
2. Každý z vrcholů má právě jednoho potomka. Písmeno-vrcholy mohou mít kromě svého potomka
ještě i omezený počet ”sousedů”. Nemusí se jednat však o přímé sousedy, ale o všechny takové
písmeno-vrcholy, spolu s kterými daný vrchol vytváří malý AVL strom.
3. Cesta od kořene ke každému vrcholu lze rozdělit do vrstev: každou vrstvu tvoří buďto slovovrchol, nebo několik písmeno-vrcholů ”sousedících” v jednom AVL stromu. Spojnice mezi vrstvami je vždy přes odkaz na potomka. Tedy například:
- slovo-vrchol (vrstva n) ⇒ slovo-vrchol (vrstva n+1) ⇒ slovo-vrchol (vrstva n+2),
- slovo-vrchol (vrstva n) ⇒ kořen AVL stromu (vrstva n+1) → cesta v AVL stromu po
”sousedech” kořenu (vrstva n+1) → písmeno-vrchol reprezentující znak (vrstva stále n+1)
⇒ slovo-vrchol (vrstva n+2),
kde ⇒ označuje odkaz na potomka, a → označuje přechod po ”sousedech” písmeno-vrcholech
v rámci jednoho AVL stromu v jedné vrstvě.
4. Hodnota reprezentovaná vrcholem je rovna zřetězení hodnot vrcholů (řetězců obsažených ve
slovo-vrcholech a znacích obsažených v písmeno-vrcholech) ležících na cestě od kořene celého
stromu směrem k danému vrcholu včetně. Zřetězují se hodnoty pouze takových vrcholů z každé
vrstvy, z nichž vede přímý odkaz na potomka do nižší vrstvy, kterou cesta prochází.
35
5. AVL strom písmeno-vrcholů obsahuje od každého povoleného znaku abecedy nejvýše jednu
hodnotu. AVL strom ve vrstvě N – mající tedy otce ve vrstvě N-1 – slouží jako výhybka pro
slova, která mají společný prefix o délce N-1, ale na pozici N se liší ve znaku.
6. Existuje-li slovo-vrchol ve vrstvě N+1 obsahující řetězec A délky L, pak ve stromě neexistuje
takové slovo B, které by mělo s daným slovem shodný prefix o délce větší než N a menší než N+L.
Což říká jinými slovy toto: oproti klasickým triím zde slovo-vrcholy tvoří ”zkratky” takových
větví trie, které by měly pouze jednoho potomka. V hodnotě slovo-vrcholu je uložena tato cesta.
36
Obrázek 3: Vrstevnatý model stromu
37
Obrázek 4: Příklad uložení hodnot: MOJECI, MOJECISLO, MOJEHODNOTA, MOJEPROMENNA
38
Datová struktura kořenu stromu jmen proměnných je definována strukturou Tpromtree. V ní je
odkaz na nejvýše postavený vrchol stromu (typu slovo-vrchol, nebo písmeno-vrchol) v unii TpromtreePismSlov. Tato unie je schránka zahrnující v sobě strukturu pro slovo-vrchol TpromtreeSlov a písmenovrchol TpromtreePismSlov. Navzájem se rozliší atributem zn. Písmeno-vrchol v atributu zn obsahuje
znak, který reprezentuje, zatímco slovo-vrchol má atribut zn roven nule.
Ve struktuře TpromtreeSlov je řetězec uložen v poli r0 o délce definované pomocí makra s názvem
BUFSIZE_TPROMTREESLOV. Pokud je řetězec delší než toto pole, je v něm uložen prefix této délky a zbytek
je dynamicky uložen v r1. Toto má výhodu jediné alokace pro krátké řetězce, při alokování nové struktury
TpromtreeSlov.
Obě struktury TpromtreePism a TpromtreeSlov mají odkaz na potomka. Byla-li do stromu vložena
hodnota, kterou reprezentuje daný vrchol, je jeho atribut indtree nenulový a odkazuje na strom definovaných indexů TpromennaKoren. Avšak vrchol může ve stromě existovat, třebaže nereprezentuje žádnou
hodnotu a je jen jakýmsi stavebním kamenem.
39
Obrázek 5: Struktury stromu Tpromtree
40
5.1.5. Přidávání prvku do stromu Tpromtree
Algoritmus je v jádru jednoduchý, jeho cílem je minimalizovat počet vrcholů, z nichž se budoucí
strom skládá, zároveň volí cestu menší spotřeby paměti - minimálního alokování nových struktur a
”recyklování” starých struktur. Tímto je zachována dostatečná rychlost při přístupu.
Algoritmus VLOŽ (řetězec, adresa odkazu na unii) je rekurzívní, postupuje shora dolů. Jednotlivé
kroky mají tyto parametry:
- řetězec pro vložení,
- dvojitý ukazatel na unii TpromtreePismSlov, kam se bude vkládat.
Pokud je řetězec nulový, algoritmus končí.
Označení: Vkládaný řetězec Ř, jeho délka L a dvojitý ukazatel na unii U, řetězec uložený v unii
URET.
1. Je-li L = 0, konec.
2. Je-li L = 1 a *U = 0, vlož Ř jako TpromtreePism do *U, konec.
3. Je-li L > 1 a *U = 0, vlož Ř jako TpromtreeSlov do *U, konec.
4. Je-li L = 1 a *U 6= 0 a typ(*U) = TpromtreePism, vlož Ř jako TpromtreePism do AVL stromu
*U, pokud tam ještě není. Konec.
5. Je-li L > 1 a *U 6= 0 a typ(*U) = TpromtreePism, hledej Ř[0] v AVL stromu *U, neexistuje-li
vytvoř jej. Do U přiřaď adresu položky potomek u právě nalezeného/vloženého prvku v AVL
stromu. Ř zkrať o první znak. Rekurzivní volání VLOŽ (Ř, *U)
6. Je-li L ≥ 1 a *U 6= 0 a typ(*U) = TpromtreeSlov a Ř[0] 6= URET[0], vytvoř nový AVL strom
obsahující struktury TpromtreePism (označme je PismŘ, PismU) pro oba první znaky řetězců
Ř a URET. Oba řetězce zkrať o první znak.
Volej rekurzivně VLOŽ (Ř, adresa potomka PismŘ)
Je-li délka URET = 0, dealokuj strukturu *U. Vrchol nově vzniklého AVL stromu přiřaď do *U.
Konec
7. Je-li L ≥ 1 a *U 6= 0 a typ(*U) = TpromtreeSlov a ∃i > 0, ∀0 < x < i, Ř[x] 6= U RET [x]
a Ř[i] = U RET [i], vytvoř nový AVL strom obsahující struktury TpromtreePism (označme je
PismŘ, PismU) pro oba první neshodné znaky řetězců Ř a URET.
Nyní záleží na původní délce řetězce URET, rozhodují se dvě možnosti: Zda je delší prefix, kde
se znaky shodují, nebo zda je delší část řetězce až za pozicí i. Kvůli minimalizaci alokované paměti se
použije původní unie *U buďto pro prefix, za který bude zavěšen AVL strom nebo pro prvek reprezentující
původní hodnotu, který se zavěsí za PismU. Druhá struktura se musí doalokovat: prefix, nebo potomek
pro PismU. Strukturu reprezentující shodný prefix řetězců vlož do *U. Volej rekurzivně VLOŽ (Ř, adresa
potomka PismŘ).
Konec
Algoritmus je implementován funkcí
TpromtreePismSlov* vloz_Wstromret(const char *x,
TpromtreePismSlov ** p)
Úspoře alokování přispívá statické pole r0 struktury TpromtreeSlov, kde je ukládán řetězec až do
délky BUFSIZE_TPROMTREESLOV Zbytek je alokován dynamicky do položky r1.
41
5.1.6. Odebírání prvku ze stromu Tpromtree
Algoritmus je také prostý. Po odebrání požadovaného prvku provádí kontrolu celého stromu, dbá
aby:
1. Ve stromě nezůstávaly slepé větve, tzn. složené pouze z vrcholů nereprezentujících žádný prvek
(atribut indtree je u nich nulový),
2. Cesty neobsahovaly zbytečné vrcholy. Úseky větví dlouhých větví bez odboček nahrazuje strukturou TpromtreeSlov do níž vloží celý řetězec reprezentovaný větví.
Algoritmus tedy po odebrání prvku zajišťuje odebrání zbytečných vrstev na cestě k odebíranému
prvku a odstranění slepých cest.
Algoritmus je začleněn ve funkci
int delete_Wstrom_uzel_index(const char * jmeno,
TpromtreePismSlov ** p,
const Tpromindex index)
Funkce nalezne prvek reprezentující jmeno ve stromě Tpromtree * strom_prom. Existuje-li odebere ze stromu indexů položky jmeno z atributu indtree hodnotu reprezentovanou indexem index.
Byl-li odebrán poslední index, je zahájen výše popsaný algoritmus pro odebrání prvku jmeno.
Následující obrázky demonstrují některé situace:
První situace ukazuje strom, kde jsou reprezentovány pouze řetězce ”AHO” a ”AHOJKY”. ”A” je
pouze konstrukční vrchol, nemá definován obsah, jeho indtree = 0. Po odebrání řetězce ”AHO” dojde
ke krácení větve.
Druhá situace ukazuje strom, kde jsou reprezentovány pouze řetězce ”AHOJKY” a ”PROM”. Vrcholy ”A”, ”AHO”, ”P” jsou pouze konstrukční vrcholy, nemají definovaný obsah. V důsledku odebrání
řetězce ”AHOJKY” je rušena celá levá větev odspodu, až k AVL stromu pro vrcholy ”A” a ”P”, ”A” je
zrušen také. Následuje krácení jediné nyní existující větve. Zůstává řetězec ”PROM”.
42
Obrázek 6: Krácení délky větve.
43
Obrázek 7: Shlukování sousední větve, pokud se stávající stala slepou. (Vrcholy ”A” i ”P” spolu tvoří AVL strom
44
5.2. Funkce pro práci s proměnnými
Následuje popis hlavních funkcí sloužících k ukládání proměnných, získávání hodnot z proměnných
a jejich iterování. Pozn.: Podrobné popisy parametrů funkcí jsou uvedeny v hlavičkovém souboru mivar/promenne.h
5.2.1. Funkce pro vkládání hodnot
Do proměnných lze uložit hodnotu ve formátu:
- původního SQL typu,
- textového řetězce.
Druhý případ v sobě zapouzdřuje převod řetězce na SQL typ SQLCHAR, a jeho následovné uložení
jako v první variantě.
Vložení předem vytvořené hodnoty typu TpromtypeSQL * jako hodnotu do stromu definovaných
proměnných provádějí funkce
int uloz_prom_SQL (const char * jmeno,
TpromtypeSQL * prom)
int uloz_prom_ar_SQL (const char * jmeno,
const Tpromindex index,
TpromtypeSQL * prom)
První ukládá na indexovou pozici jedna, druhá ukládá na pozici zadanou parametrem index. Byla-li
před tím již nějaká proměnná definována, je přepsána a její typ změněn.
Pro vložení řetězce specifikované délky složí funkce
int setprom (const char * jmeno, const char * data,
const unsigned int delka)
int setprom_ar (const char * jmeno,
const Tpromindex index,
const char * data,
const unsigned int delka)
První funkce vkládá na indexovou pozici jedna, druhá na pozici zadanou parametrem index.
5.2.2. Funkce pro získávání hodnot
Při běhu programu se z proměnných ve většině případů získávají hodnoty již jen v podobě textových
řetězců, nezávisle na jejich původním typu. Je třeba jen rozlišit, zda v proměnné nebyla hodnota typu
SQLNULL. S proměnnými tohoto typu se v několika případech nakládá jinak.
K tomuto účelu je definován návratový typ Tprom_bunka, obsahující položky
- char * hodnota,
- unsigned int delka,
- char null,
Při null rovném jedné, představuje struktura typ SQLNULL, jinak jde o řetězec v položce hodnota
o délce delka.
45
Funkce vracející hodnotu proměnné ze stromu proměnných provádějí vnitřní konverze na řetězcový
formát.
Jsou to
Tprom_bunka * getprom (const char * jmeno,
const int warn,
Tdata_dur * stdven)
Tprom_bunka * getprom_ar (const char * jmeno,
const Tpromindex index,
const int warn,
Tdata_dur * stdven)
Opět jde o dvě varianty téže funkce. První vrací hodnotu z indexové pozice jedna, druhá ze zadané.
Implicitní konverze provádí následujícím způsobem:
- Pro typ SQLNULL vrací hodnotu proměnné $MI_NULL, je-li definována. Pokud není definována
vrací řetězec ”NULL”. Navíc ve vrácené struktuře nastavuje příznak null na hodnotu 1.
- Pro typ SQLCHAR, SQLVCHAR vrací hodnotu řetězce. Pokud je nastavena globální proměnná
mivar_istrim, provádí navíc ořez mezer zprava.
- Pro typ SQLSMINT, SQLINT, SQLSERIAL vrací celé číslo v dekadické podobě.
- Pro typ SQLDATE vrací datum ve formátu {DD.MM.YYYY
:
:
.
}, kde DD značí dvoj-
ciferný zápis dne, MM dvojciferný zápis čísla měsíce a YYYY čtyřmístný zápis roku.
- Pro typ SQLDTIME vrací datum a čas ve formátu {DD.MM.YYYY hh:mm:ss.12345}, kde DD
označuje dvojciferný zápis dne, MM dvojciferný zápis čísla měsíce a YYYY čtyřmístný zápis
roku, hh dvojciferný zápis hodin, mm dvojciferný zápis minut, ss dvojciferný zápis sekund. Čísla
1, 2, 3, 4, 5 označují prvních pět cifer v desetinném rozvoji sekund. Lze tedy vracet hodnoty
s přesností na desetitisíciny sekund. Nedefinované cifry jsou nahrazeny znakem X, protože v
databázi lze nastavit přesnost položky typu SQLDTIME.
- Pro typ SQLDECIMAL vrací dekadicky zapsané destinné číslo s přesností udanou globální proměnnou PocetDesetinnychMist
- Pro typ SQLINTERVAL vrací časový interval ve formátu Y;M;D;h;m;s;1;2;3;4;5, kde Y udává
roky, M měsíce, D dny, h hodiny, m minuty, s sekundy a čísla 1, 2, 3, 4, 5 určují desetinnou část
sekund na 5 desetinných míst. Nedefinované položky obsahují mezery.
Pokud jsou funkce getprom nebo getprom_ar volány uvnitř tagu MIVAR s definovaným atributem
DEFAULT a odkazovaná proměnná není definována, je vrácena hodnota defaultní. Ta je parserem tagu
MIVAR uložena v mivar_def. Podobně uvnitř tagu MISQL s definovanou defaultní hodnotou je v případě
nenalezení hledané proměnné vrácena hodnota defaultní. Pro tento stav musí být definován SQL prefix
MI_SQLnamespacePrefix a úroveň zanoření MISQL tagů jsem_v_misql musí být kladná. Pak vznikne
jméno proměnné obsahující defaultní hodnotu konkatenací makra STANDART_SQL_DEFAULT_VALUE a SQL
prefixu.
Hodnotu proměnné v SQL tvaru vracejí funkce
TpromtypeSQL * getprom_SQL (const char * jmeno,
const int warn, Tdata_dur * stdven)
TpromtypeSQL * getprom_ar_SQL (const char * jmeno,
const Tpromindex index, const int warn,
46
Tdata_dur * stdven);
Funkce vracejí kopii hodnoty uložené ve stromu proměnných. Druhá funkce vrací pro konkrétní
index.
Pro vrácení pouhého odkazu na SQL strukturu ve stromu proměnných slouží funkce
TpromtypeSQL * get_uknaprom_ar_SQL (const char * jmeno,
const Tpromindex index,
const int warn, Tdata_dur * stdven)
Pokud proměnná obsahuje číslo, lze jej získat přímo jako hodnotu typu UCislo nebo s předpokladem
pouze celočíselného obsahu, jako long pomocí následujících funkcí:
UCislo getprom_UCislo_ar(const char * jmeno,
const Tpromindex index, const int warn,
int * indikace, Tdata_dur * stdven)
long getprom_cislo_ar(const char * jmeno,
const Tpromindex index, const int warn,
int * indikace, Tdata_dur * stdven)
5.2.3. Informační funkce
Důležitou funkcí je test, zda je daná proměnná definovaná, provádí to
char issetprom(const char * jmeno)
a její varianta pro specifický index
char issetprom_ar(const char * jmeno,
const Tpromindex index)
První obsazený index proměnné vrací
Tpromindex getprom_prvni_index(const char * jmeno)
Počet obsazených indexů lze získat s
Tpromindex getprom_pocet_polozek(const char * jmeno)
První volný prvek za nejvyšším obsazeným indexem proměnné ukáže funkce
Tpromindex getprom_prvni_volny_index(const char * jmeno)
Nejbližší vyšší (další) obsazený index vrací
Tpromindex getprom_next_id(const char * jmeno,
const Tpromindex index)
Pomocí těchto čtyř výše uvedených funkcí lze provádět iterování definovanými indexy proměnné.
5.2.4. Funkce dealokování
Odnastavení a uvolnění hodnoty proměnné na specifikovaném indexu provede funkce
void deleteprom_ar(const char * jmeno,
const Tpromindex index)
47
Při ukončení parsování tagu MISQL je důležité smazat všechny proměnné začínající na určitý prefix.
K tomuto účelu je zde funkce
void dealok_prom_prefixcislo(char * prefixName)
Celý strom definovaných uživatelských proměnných strom_prom odalokuje funkce
void dealoktree()
Návratové hodnoty funkcí získavajících obsah proměnné převedený na řetězec Tprom_bunka jsou
uvolňovány funkcí
void dealok_Tprom_bunka(Tprom_bunka * p)
5.2.5. Parsování proměnné
Při parsování vstupního proudu je často používaná funkce
char * mivar_parse1prom_uv(TParsPozice * i,
Tpromindex * index, char uvozovky,
Tdata_dur * stdven)
Ta se pokusí z pozice ve vstupu i načíst jméno proměnné a index, s nímž je proměnná uvedena.
Její obdoba je
char * mivar_parse1prom(TParsPozice * i,
Tpromindex * index,
Tdata_dur * stdven)
Ta však parsuje proměnnou jejíž jméno smí být uvedeno v uvozovkách nebo apostrofech.
Funkce pro přeskočení zápisu jména proměnné spolu s indexem je
char mivar_parse2prom_uv(TParsPozice * i,
Tpromindex * index,
char uvozovky,
Tdata_dur *vystup)
Funkce jméno a index přejde bez vyhodnocení.
Pro vrácení číslené hodnoty naparsované proměnné uvedené ve vstupním proudu na specifikované
pozici slouží funkce:
UCislo mivarparse_getprom_UCislo(TParsPozice * i,
const int warn, int * indikace,
char uvozovky, Tdata_dur * stdven)
long mivarparse_getprom_cislo(TParsPozice * i,
const int warn, int * indikace,
char uvozovky,
48
Tdata_dur * stdven)
První vrací desetinná čísla, druhá lze užít pouze pro získání předpokládaného celočíselného obsahu.
49
6. Funkce
Parser předdefinovaných funkcí je implementován ve funkci
int mifce(TParsPozice *pozice, Tdata_dur *vystup, Tdata_dur *warning);
Průběh parsování lze rozdělit na tyto po sobě jdoucí části:
1. Úvodní nastavení a inicializace
2. Parsování názvu funkce
3. Zpracování parametrů funkce
4. Zpracování požadavku funkce
Pozn.: Deklarace funkce je v hlavičkovém souboru mifce/mifce.h, definice těla funkce společně s
pomocnými funkcemi je v souboru mifce/mifce.c. Funkce pro konkrétní zpracování požadavku předdefinované funkce jsou deklarovány v souboru mifce/fce.h, jejich definice jsou v souboru mifce/fce.c
6.0.1. Úvodní nastavení a inicializace
Funkce mifce potřebuje parametr pozice ve vstupním proudu , kde má začít parsovat (pozice),
dynamický řetězec pro uložení výstupu funkce (vystup) a dynamický řetězec pro výpis chyb (warning).
Je-li parametr vystup nulový, je výstup funkce posílán přímo na standardní výstup. Stejně je řešeno
předávání chyb (je-li warning nulový, jsou chyby posílány na výstup). Rodičovská parsovací funkce
zpracuje úvodní řetězec funkce: ”$(”. Pozice předaná v parametru odkazuje na první znak za tímto
řetězcem.
Kromě parametrů funkce obdrží i odkaz na uloženou pozici prvního znaku funkce, tedy znaku $.
Hodnota pozice je uložena v globální proměnné michyba_pozice, a je nastavovaná rodičovskou funkcí.
Její hodnota je uložena do globální proměnné chyba_zacatek.
Uložená pozice prvního znaku tagu je využívána při hlášení některých chyb (např. předčasný konec
souboru), kde se vypíše spolu s chybou jako pozice začátku funkce, ve kterém došlo k chybě. Více
informací o ukládání chybové pozice pomocí proměnné michyba_pozice viz. kapitola o zpracování chyb.
V parametru warning je odkaz na dynamický řetězec pro vypisování chyb. Ten se vkládá do volání
funkce chybového výpisu michyba(). U vážných chyb se ale nepředává, protože ty jej stejně nepoužívají
a vypisují přímo na výstup.
6.0.2. Parsování názvu funkce
Po úvodní inicializaci je naparsován název funkce. Ignorují se počáteční a koncové bílé znaky. Název
je uložen do lokální proměnné nazev. Další běh programu je řízen pomocí hlavního přepínače switch,
který rozdělí funkce do podskupin podle počátečního písmena. Tím je docíleno jak zpřehlednění kódu,
tak zvýšení rychlosti výpočtu díky redukci počtu porovnání celých řetězců.
6.0.3. Zpracování parametrů funkce
50
Každá předdefinovaná funkce provádí zpracování parametrů samostatně s ohledem na jejich očekávaný typ a počet.
Předdefinované funkce lze rozdělit na podskupiny podle počtu očekávaných parametrů na funkce:
1. s volitelným počtem parametrů (minimálně však jedním)
2. s pevným počtem parametrů
3. bez parametrů
Parametry funkcí s volitelným počtem parametrů se zpracovávají v cyklu každý zvlášť a v každé
iteraci je ihned generován mezivýsledek. Pro jejich načítání je použita funkce
int zpracuj_arg(Tdata_dur *warning, TParsPozice *pozice, Tdata_dur **arg);
Funkce dostává pozici na počátečním znaku parametru. Tento parametr načte a vyhodnotí případné
proměnné nebo vnořené funkce (rekursivním voláním mifce() ). Text v uvozovkách nebo apostrofech
není interpretován, zdvojení znaků "(uvozovka), ’(apostrof), $(dolar) ruší jejich specialní význam.
Pokud funkce narazí na tag <?MIINFO>, zavolá funkci miinfo(), která jej přeskočí a vrátí ukazatel na pozici
za koncovým tagem <?/miinfo>.
Při úspěchu vrátí funkce zpracuj_arg() naparsovaný řetězec v parametru arg
Návratová hodnota funkce je následující:
- 0 - zpracování proběhlo bez chyb a skončilo načtením čárky (následuje další parametr)
- 1 - zpracování proběhlo bez chyb a skončilo načtením zavorky ) (konec funkce)
- 2 - syntaktická chyba, parametr po jeho přeskočení skončil čárkou
- 3 - syntakticka chyba, parametr po jeho přeskočení skončil závorkou )
- 10 - fatální chyba (předčasný konec souboru)
Pro funkce s pevným počtem parametrů je využita funkce
int nacti_parametry(Tdata_dur *warning, TParsPozice *pozice, const char *nazev,
const char *typ, ...)
Vstupními parametry funkce nacti_parametry() je kromě dynamického řetězce pro chybový výstup
a počáteční pozice i název předdefinované funkce, jejíž parametry se zpracovávají (použit v chybových
hlášení), dále řetězec nesoucí informaci o typu a počtu načítaných parametrů a ukazatele na datové
struktury pro uložení konkrétních načtených parametrů.
Délka řetězce typ odpovídá počtu načítaných parametrů. Jejich typ je v této proměnné uložen ve
tvaru jednopísmenné zkratky - s=string, i=celé číslo, f=realné číslo, d=datetime (řetězec o délce 27
znaků), c=char. Číselné parametry se načítají do struktury UCislo.
K zpracovávání jednotlivých parametrů je využito výše popsané funkce zpracuj_arg().
Po načtení parametru je provedena jeho typová kontrola. V případ neshody typu jsou všechny dosud
načtené parametry uvolněny a běh fuknce končí nenulovou návratovou hodnotou.
6.0.4. Zpracování požadavku funkce
51
Po získání očekávaných parametrů jsou volány příslušné funkce (většinou z mifce/fce.c), které zpracují požadavek předdefinované funkce. Výsledek je pak předán do dynamického řetězce vystup (je-li
nenulový), nebo vypsán na standartní výstup funkcí iprintf().
52
7. Aritmetika
53
7.1. Desetinná čísla
Pro uživatele je k dispozici aritmetika s plovoucí desetinnou čárkou. V této kapitole je popsán typ
UCislo a funkce, které s ním pracují a makra která ho specifikují. Rovněž i funkce pro vyhodnocení a
parsování aritmetického výrazu.
Pozn.: Podrobné popisy funkcí a jejich parametrů jsou uvedeny v hlavičkovém souboru miblock/plus.h
a pro zpracování aritmetického výrazu v mivar/promenne.h
54
7.2. Desetinná čísla
7.2.1. Uložení a popis čísel
Desetinná čísla slouží k uchování číselných hodnot, které ukládá uživatel do proměnné, nebo které
jsou do proměnné uloženy z databáze jako typ DECIMAL. Všechna čísla nebo aritmetické výrazy, které se
mohou objevit na očekávaných místech (jako index u proměnné, parametr aritmetické funkce, podmínka
v atributu COND apod.) se na tento typ převádějí a v něm jsou také vyhodnocovány.
Hlavním typem pro uložení desetinného čísla je typ TCislo. Na většině míst je používán jen ukazatel
na něj - UCislo.
Typ je blíže specifikován makry:
- TCISLO_MAX_DESETINNYCH_MIST - maximální počet desetinných míst,
- TCISLO_MAX_DELKA_EXPONENT - maximální počet cifer exponentu
Skutečný počet desetinných míst, s nímž uživatel právě pracuje je neustále roven prvnímu uvedenému makru, počet desetinných míst, které se vždy vypíší na výstup je řízen proměnnou PocetDesetinnychMist. Tuto hodnotu může uživatel měnit uživatelskou funkcí.
Původně byla připravena implementace na libovolný počet desetinných míst. Z důvodu urychlení
výpočtu a především z důvodu, že na stránkách aplikace nebude ve většině případů potřeba větší přesnosti
než na 15 desetinných míst a že velikost čísla nebude také přesahovat 1099 byl pro reprezentaci použit
vnitřní typ double.
Funke uvedené níže jsou psány obecně a slouží jako interface pro přístup k typu UCislo, jejich těla
je proto možné spolu s typem TCislo přeimplementovat na libovolnou aritmetiku.
7.2.2. Funkce pro inicializaci, uvolnění a vkládání hodnot
Na strukturu se pohlíží obecně. Před prvním použitím je třeba ji alokovat a inicializovat, po ukončení
používání opět uvolnit.
K alokaci struktury UCislo a její inicializaci na konkrétní hodnotu lze použít několik možných
funkcí:
- Inicializace pomocí řetězce obsahujícího číslo:
UCislo init_TCislo(char *s, Tdata_dur * vystup)
- Inicializace pomocí hodnoty SMINT:
UCislo init_TCislo_SQLSMINT(const int s,
Tdata_dur * vystup)
- Inicializace pomocí hodnoty INT:
UCislo init_TCislo_SQLINT(const long int s,
Tdata_dur * vystup)
Tyto funkce alokují zcela novou strukturu UCislo, do již existující struktury se nová hodnota dá
vložit řetězcem přes funkci
char set_TCislo(char *s, UCislo * a, Tdata_dur * vystup)
Dealokaci provádí funkce
55
void free_TCislo(UCislo a)
Kopírovací konstruktor je
UCislo TCislocpy(const UCislo a, Tdata_dur * vystup)
7.2.3. Aritmetické funkce
Funkce pro základní aritmetické operace v sobě sami indikují přetečení, nebo dělení nulou a vyvolávají chybové hlášení. Pro chybový výstup slouží parametr vystup. V případě chyby vracejí nulu.
Funkce pro sčítání, odčítání, násobení a dělení, a pro unární mínus:
UCislo nasob_TCislo(const UCislo a, const UCislo b,
Tdata_dur * vystup)
UCislo del_TCislo(const UCislo a, const UCislo b,
Tdata_dur * vystup)
UCislo plus_TCislo(const UCislo a, const UCislo b,
Tdata_dur * vystup)
UCislo minus_TCislo(UCislo a, UCislo b,
Tdata_dur * vystup)
void unarni_minus_TCislo(UCislo a,
Tdata_dur * vystup)
Funkce testující, zda je číslo rovno nule:
char isnull_TCislo(const UCislo a)
7.2.4. Funkce pro parsování čísla
Následující funkce přečte číslo ze vstupního proudu na zadané pozici, a vrátí jej:
UCislo TCislo_parse(TParsPozice *i, Tdata_dur * vystup)
Tato funkce přeskočí libovolné číslo ze vstupního proudu:
int skip_TCislo(TParsPozice *i, Tdata_dur * vystup)
7.2.5. Konverzní funkce
Typ UCislo lze konvertovat na řetězec, na index proměnně, na číselné typy int a double. Při konverzi na řetězec je číslo vypisováno s přesností na PocetDesetinnychMist desetinných míst. Při konverzi
na int a index proměnné Tpromindex je vypisována chybová hláška, došlo-li k přetečení. Maximum pro
int je definováno jako INT_MAX, maximum pro index proměnné je v MAX_TPROMINDEX.
Funkce
char * TCislo2char(const UCislo cis, const char exp,
Tdata_dur * vystup)
alokuje nový řetězec s číslem. Podobnou činnost provádí i funkce
int TCislo2char_buf(char* buf,
const unsigned int buf_del,
const UCislo cis,
56
const char exp,
Tdata_dur * vystup)
která výsledné číslo uloží do předem alokovaného bufferu o pevné délce.
Převod na int zajišťuje funkce:
int TCislo2int(UCislo c, int * vysledek,
Tdata_dur * stdven)
na double:
int TCislo2double(UCislo c, double * vysledek,
Tdata_dur * stdven)
a na index proměnné Tpromindex:
char TCislo2Tpromindex(const UCislo a,
Tpromindex * i,
Tdata_dur * stdven)
57
7.3. Aritmetický výraz
Aritmetický výraz je vyhodnocován jen ve výrazu indexu proměnné při jejím volání.
(Př.: $prom[12+$index+(2*$(+,4,3))])
Na jiných místech s vyjímkou jediné uživatelské funkce předpokládán není. Tzn.: na jiných místech
v aplikačních stránkách je považován za textový řetězec.
Výraz smí obsahovat číselné konstanty, proměnné, funkce, prioritní kulaté závorky. Dále operátory
sčítání, odčítání, násobení, dělení a operátor unárního mínusu.
Výsledek aritmetického výrazu je vrácen jako UCislo. Libovolné chyby v syntaxi, nebo chyby vzniklé
při výpočtu jsou hlášeny funkcí michyba() a nastavením indikační proměnné chybavyr na nenulovou
hodnotu. Rekurzivně volané funkce si mezi sebou předávají pozici ve vstupním proudu, odkud parsují a
také dynamický řetězec pro hlašení chyb. Při parsování jsou ignorovány bílé znaky (mezery, tabelátory,
odřádkování, . . .)
58
7.4. Vyhodnocování
Výraz je vyhodnocován rekurzivními funkcemi. Každá funkce zpracovává jednu prioritní úroveň.
Funkce jsou definovány v mivar/prom aritvyr.c.
Prioritní úroveň sčítání a odčítání:
UCislo vyraz_ar(Tdata_dur * stdven, TParsPozice * s)
UCislo vyraz_ar_uv(Tdata_dur * stdven, TParsPozice * s,
char uvozovky)
V parametru uvozovky je informace, zda parsovat jména proměnných obsahující uvozovky a apostrofy nebo ne. První funkce parsuje s nimi. Prioritní úroveň násobení a dělení:
UCislo clen_ar(Tdata_dur * stdven, TParsPozice * s,
char uvozovky)
Prioritní úroveň konstant, proměnných, funkcí:
UCislo fakt_ar(Tdata_dur * stdven, TParsPozice * s,
char uvozovky)
Zde je také zpracován vnořený výraz uzavřený v kulatých závorkách a operátor unární mínus. Pro
parsování funkcí a proměnných je volána funkce
UCislo vrat_ar(Tdata_dur * stdven, TParsPozice * i,
char uvozovky)
59
7.5. Parsování bez vyhodnocování
Přeskok aritmetického výrazu od dané pozice ve vstupu, s přesměrováním chybového výstupu do
dynamického řetězce zajišťuje funkce
char vyraz_ar_uv_parse(TParsPozice * s,
char uvozovky,
Tdata_dur *vystup)
V parametru uvozovky lze opět nastavit, zda proměnné parsovat i s uvozovkami a apostrofy v jejich
jménech nebo nikoliv.
Funkce pro přeskok výrazu bez vyhodnocení pracuje obdobně jako funkce pro vyhodnocení výrazu.
Pro každou rekurzívní úroveň existuje funkce, která ji zpracovává.
60
8. Bufferování vstupu
Vstupní bufferování slouží především k ukládání (cacheování) stránek vstupních souborů, a vkládání řetězců a souborů do sebe. Pozn.: Deklarace struktur, maker a funkcí je v hlavičkovém souboru
mivar/vstupbuf.h, definice těl funkcí mivar/vstupbuf.c
Hlavním cílem systému vstupního bufferování je:
- Ukládání částí vstupních souborů v paměti, aby se zabránilo zbytečným přístupům na disk,
zvláště při vykonávání cyklů v programu.
- Podpora vkládání souborů a textových řetězců do libovolného jiného souboru nebo textového
řetězce.
- Zajištění lineárního přístupu k vloženým strukturám, dle jejich vnitřního zanoření.
- Evidování čísla řádky a čísla znaku v rámci řádky, pro aktivní pozici ve vstupním proudu.
- Optimalizace pro čtení znaků neklesající posloupnosti pozic, počínaje jedničkou, se skoky pouze
na dříve označené pozice.
- Zajištění samovolného uvolňování nepotřebných a již zbytečných struktur.
61
8.1. Struktura systému vstupního bufferování
Systém se skládá ze
- správce vložených souborů,
- správce cacheovaných stránek souborů,
- stromu vložených souborů a řetězců.
8.1.1. Správce vložených souborů
Všechny vložené soubory je třeba evidovat celým číslem a jménem. Podle čísla souboru se uchovávají
v paměti jeho používané stránky.
Souborů zároveň vložených do sebe se nepředpokládá mnoho, je pro ně připraveno hashovací pole
modulo pořadí vkládání. Pole je velikosti VSTUP_BUF_FILE_HASH (doporučená hodnota je 10).
Obsahuje ho struktura pro registraci definovaných souborů TBfilehead, kromě zmíněné položky
TBfile * prvni[VSTUP_BUF_FILE_HASH] pro pole, obsahuje ještě položku posl_id s číslem posledního
vloženého souboru.
Konfliktní soubory se zavěšují do spojového seznamu za společný prvek v hashovacím poli.
Soubor je definován strukturou TBfile, obsahuje tyto položky:
- jmeno - jméno souboru s cestou,
- id - přidělená celočíselná identifikace souboru,
- delka - celková délka souboru,
- pocet_odkazu - celkový počet existujících odkazů struktur TBvirtfile na tuto položku, udává
mimo jiné kolikrát je soubor již vložen do struktury,
- void * file - odkaz na strukturu reprezentující aktuální otevření souboru v operačním systému nebo v API serveru Apache. Jedná se o implementačně závislý hendler,
- next - další záznam v konfliktním spojovém seznamu.
Zda se jedná o filesystém, který je case senzitivní je nutno specifikovat pomocí makra
VSTUP_BUFF_FS_CASE_SENSITIVE.
Strom registrovaných souborů je v globální proměnné TBfilehead Bfilehead.
Registraci nového souboru do této struktury provádí funkce
int vb_registrace_TBfile(char * jmeno,
unsigned int delka,
void * file)
Je nutno předat již otevřený soubor, jeho délku a odkaz na strukturu reprezentující otevření daného
souboru. Funkce zařadí nový záznam do hashovacího pole a vrátí ID souboru.
Se soubory se pracuje prostřednictvím tří funkcí. V jejich těle je definována struktura aktuálního
otevření souboru (je vrácena při otevření souboru). Odkaz na tuto strukturu používají při načítání dat
ze souboru a jeho zavírání.
Jsou to funkce:
void* vb_otevrisoubor(char * jmeno,
unsigned int *delka)
62
Otevře soubor a vrátí odkazem jeho délku spolu se strukturou reprezentující aktuální otevření souboru pouze pro čtení.
void vb_zavrisoubor(void * file)
Zavře soubor reprezentovaný strukturou.
int vb_napln_stranku(char * buffer, void * file,
unsigned int startpozice,
unsigned int delka)
Přečte data o specifikované délce ze souboru reprezentovaného strukturou od určené pozice. Data
uloží do pole buffer.
Tyto funkce mají svoji konkrétní implementaci v souboru cgi.c a mod_ifmx.c v závislosti na
platformě.
8.1.2. Správce uložených stránek souborů
V paměti se obsah jedné stránky udržuje ve struktuře TBpage. Každá stránka má u sebe tyto položky:
- idfile - ID souboru, ke kterému patří,
- obsah[VSTUP_BUFF_PAGESIZE] - data stránky souboru,
- startpoz - pořadí stránky v souboru,
- delka - obsazený počet bytů v položce obsah,
- pristup_pred, pristup_po - sousední stránky v obousměrném spojovém seznamu, jedná se o
spojový seznam naposledy použitých stránek,
- hash_pred, hash_dal - sousední stránky v obousměrném spojovém seznamu, jedná se o spojový
seznam umístěný v hashovacím poli podle ID souboru a čísla stránky.
Každá stránka má pevnou velikost VSTUP_BUFF_PAGESIZE definovanou na hodnotu 4096, proto si
stačí u každé stránky pamatovat její pořadí. Položka delka je zde kvůli poslední stránce.
Alokované a naplněné stránky jsou uloženy ve dvojrozměrném hashovacím poli, které umožňuje
prakticky konstantní přístup ke stránce. První dimenze hashovacího pole je pro ID souboru, má velikost VSTUP_BUF_PAGE_HASH_IDFILE nastavenou na hodnotu 10, druhá dimenze je pro pořadí stránky v
souboru, má velikost VSTUP_BUF_PAGE_HASH_IDSTR nastavenou na 5. Toto například zaručuje bez kolize
konstantní přístup ke stránkám deseti souborů o velikosti 20kB. Hashuje se modulo počet prvků v dané
dimenzi.
Urychlení hledání stránky podpoří také skutečnost, že dříve než v hashovací struktuře je kontrolována na shodu s ID souboru a číslem řádky poslední použitá stránka. Pro většinu případů je tedy
nalezení stránky konstantní čas.
Při přístupu na pole v hashovacím dvojrozměrném poli, které obsahuje na jednom políčku více
kolidujících stránek, uložených v obousměrném spojovém seznamu, se posune právě čtená stránka na
první místo.
Makro VSTUP_BUF_PAGE_COUNT v sobě zahrnuje maximální počet stránek, které smějí být naráz v
paměti alokovány.
63
Pokud je stránek požadováno více a více, musejí se nepoužívané stránky uvolňovat. K tomu slouží
druhý obousměrný spojový seznam, do kterého je každá stránka (struktura TBpage) umístěna. Při každém
přístupu na stránku, je stránka přepojena na první místo v seznamu.
Je-li potřeba některou stránku z paměti odstranit, je zvolena právě ta na konci tohoto spojového
seznamu. Uvolněna je takto stránka, na kterou bylo naposledy přistoupeno před největší dobou oproti
ostatním stránkám.
Struktura pro uložení stránek v dvojrozměrném hashovacím poli, spolu s hlavou spojového seznamu
naposledy použitých stránek je struktura TBpagehead. Její jediný exemplář je v globální proměnné
Bpagehead.
Tato struktura obsahuje i odkaz na nepotřebné uvolněné stránky. Aby se nemusely odalokovávat,
jsou uskladněny zde ve spojovém seznamu a v případě potřeby jsou z něj vyzvedávány namísto alokace.
64
Obrázek 8: Stránky v hashovací struktuře, každá ve dvou spojových seznamech.
65
8.1.3. Strom vložených souborů a řetězců
Předpokládá se možnost vkládání souborů a řetězců libovolně do sebe. Příklad skutečné situace je
na následujícím obrázku:
Obrázek 9: Ukázka vložení souborů a řetězců do sebe
Ve vnitřní reprezentaci jsou vložené soubory, stejně jako vložené řetězce ukryty ve struktuře TBvklad,
z těchto struktur je vybudován strom.
Na počátku je do globální proměnné Bbufstrom typu TBvklad vložen záznam o původním souboru,
na nějž byl serveru poslán požadavek. Další struktury do něj vložené se ukládají jako spojový seznam
setříděný vzestupně podle pozice vložení. Do nich se vkládají soubory obdobně, rekurzívně.
Pozice se ve spojovém seznamu vložených prvků ukládají relativně vzhledem k souboru, nejedná se
tedy o absolutní pozici v celém stromu.
Struktura TBvklad má následující položky:
- typ - určující zda se jedná o soubor(0) nebo řetězec(1),
- delka - vlastní délka vloženého prvku,
- delka_total - délka vloženého prvku spolu s délkami vložených prvků, rekurzívně spočítaná,
- TBvkladU obsah - vlastní obsah, odkaz na unii udávající typ prvku, zda jde o řetězec nebo
soubor,
- Tnavrat * navraty - návratové pozice, bude popsáno dále,
- maximum - aktuální pozice, odkud se naposledy četl znak,
- cisloradek - číslo řádky od začátku souboru/řetězce na aktuální pozici maximum,
- radekpozice - pořadí znaku v řádce na aktuální pozici maximum,
- TBdalsivloz * vlozeny - další vložené prvky,
66
Struktura pro další vložené prvky do daného prvku TBdalsivloz obsahuje odkaz na další prvek
ve spojovém seznamu, dále relativní pozici vložení vůči prvnímu znaku struktury, a odkaz na strukturu
TBvklad vloženého prvku.
Unie TBvkladU pro obsah v sobě zahrnuje
- strukturu pro soubor TBvirtfile obsahující jedinou položku pro ID souboru, který zastupuje
- strukturu pro řetězec TBstring obsahující odkaz na řetězec, jeho délku, a jedná-li se o chybový
hendler, je zde uloženo i jeho jméno.
Následující příklad demonstruje datové struktury a hodnoty jejich důležitých položek zachycující
soubor o délce 100 znaků s ID rovno jedné, do nějž je na pozici 20 vložen další soubor o délce 200 znaků
s ID rovným dvěma. Na pozici 60 je do něj vložen řetězec o délce 30 znaků.
Obrázek 10: Příklad vložení souboru na pozici 20 a řetězce na pozici 60 ve struktuře TBvklad.
8.1.4. Vkládání nových souborů a řetězců
Celý obsah stromu lze jednoznačně lineárně indexovat. Každý znak libovolného vloženého souboru
nebo řetězce má v rámci celého stromu svoje pořadové číslo, absolutní pozici. V rámci daného prvku
TBvklad má každý jeho znak jen relativní pozici.
Nový prvek do stromu je vkládán podle pozice absolutní. Tak, že je postupně procházeno stromem
od kořene Bbufstrom až k listu - prvku TBvklad obsahujícím ve svém intervalu relativních pozic tuto
pozici absolutní.
Pokud je ve vkládaném prvku na dané pozici volné místo, je sem vložen. Pokud je zde plno, tedy je-li
na této pozici zavěšen strom jiný, je odalokován a nahrazen nově vloženým prvkem. Odpovídá to situaci,
67
kdy v průběhu parsování v programu došlo k návratu, a tato pozice je parsována znovu. Vkládané prvky
mohou být však při každé iteraci jiné.
Vkládání prvku zajišťuje funkce
int vloz_filestring(unsigned int kam, char * soujm,
Tdata_dur * chyby,
unsigned int delkastr,
char * errorhendler)
8.1.5. Funkce igetc, návratová pozice
Při parsování se předpokládají dotazy na znaky v neklesající posloupnosti absolutních pozic. Např. 1,
2, 3, 3, 4, 4, 4, 5, 6. Pro takovéto posloupnosti dotazovaných absolutních pozic je struktura optimalizována
a dokáže si při ní evidovat číslo aktuální řádky a pozici právě čteného znaku v rámci řádky.
Znak uložený ve stromu vstupního bufferování na absolutní pozici vrací funkce:
int igetc(TParsPozice kde)
Posledně čtená pozice je v globální proměnné Bmaximum, odpovídá lokálnímu maximu neklesající
posloupnosti čtených znaků. V rámci každé struktury TBvklad je uložena aktuální relativní pozice znaku,
číslo řádky a pozice v řádce. Pokud ukazuje Bmaximum do synovské větve vložených struktur, je lokální
aktuální pozice nastavena na znak před relativní pozicí daného stromu. Pro tento znak je spočítáno číslo
řádky a pozice na řádce.
Pomocí funkce
char * vb_getJmenoSouboru(char* retezec)
lze do bufferu retezec o maximální velikosti MICHYBA_POZICECHYBY_BUF získat řetězec udávající
textový popis pozice ve stromu pro aktuální pozici Bmaximum.
Strom se projde od kořene k listu obsahujícím absolutní pozici Bmaximum, a rekurzívně se vypíše
pořadí vložení souborů a řetězců do sebe spolu s číslem řádky a pozicí v rámci řádky.
Dostane se například následující řetězec: ”Soubor ”deklarace.html”: řádek 15, pozice 23, vložený v
souboru ”uvod.html” na 8. řádku na pozici 12, vložený v souboru ”index.html” na 4. řádku na pozici 19
”
Tento řetězec je vypsán v konkrétním jazyku, dle proměnné AktualniJazyk měněné uživatelskou
funkcí.
Výpis pozice je používán společně s výpisem chybové hlášky ve funkci michyba().
Dotazy na znaky ve vstupním proudu lze klást v neklesající posloupnosti, jako parametry ve funkci
igetc. Při parsování je ale nutno očekávat cykly, tedy skoky zpět.
Získání znaku podle absolutní pozice není žádný problém, pokud by nebylo potřeba aktualizovat
číslo řádky a pozici v rámci řádku.
Proto lze dělat takovéto skoky jen na pozice, kde se již dříve tyto hodnoty znaly, a byly uloženy.
Pro danou absolutní pozici je uložena informace o počtu řádek a pořadí v rámci řádky, v každé
struktuře TBvklad, která leží na cestě od kořene stromu k listu obsahujícím znak s aktuální absolutní
pozicí.
Návratové hodnoty se ukládají ve strukturách TBvklad v podobě spojových seznamů struktury
Tnavrat a obsahují položku pro číslo řádky, pořadí znaku v rámci řádky, pozici, již reprezentují, a odkaz
68
na další prvek v seznamu. Spojový seznam je tříděn sestupně dle pozic. Struktura Tnavrat obsahuje také
příznak, zda při skoku na tuto pozici je nutné odalokovat návratové pozice z vyšších míst.
Označení si těchto pozic návratu je jednoduché. Provádí jej funkce
int vb_nova_navratova_pozice(char nedealok)
Její parametr udává, zda při skoku na tuto pozici v budoucnu mají být zrušeny návratové pozice na
vyšších pozicích.
Funkce igetc() při skoku na místa s definovanými návratovými pozicemi má opět informace o číslu
řádky a pozice v rámci ní.
8.1.6. Init struktur a dealokace
Inicializaci správce souborů, správce stránek a stromu Bbufstrom provádí funkce
int vstup_buf_init(char *jmenofile)
Funkce jako počáteční prvek stromu Bbufstrom vloží a zaregistruje soubor zadaný parametrem.
Dealokace všech struktur souvisejících s bufferováním vstupu provede funkce
void vstup_buf_destroy(void)
69
9. Zpracování chyb
70
9.1. Obsloužení chyb
Ošetření chyb je realizováno v programu dvojím způsobem:
- pomoci handlerů, viz sekci o handlerech
- pomocí nativního ošetření
V případě, že je zadefinován generický handler nebo je použit k ošetření v atributu ERR pojmenovaný handler, použije se k ošetření chyby handler, viz sekci o handlerech. V opačném případě, tj. když
není možno použít žádný handler, je vyvoláno nativní ošetření chyby.
Naivní ošetření chyby je zajišťováno funkcí michyba uvedenou v souboru michyba/michyba.h.
Zde se nejprve uloží do globální proměnné nastala_chyba číslo chyby a poté i do proměnné nastala_chyba_pozice_buf pozice vzniklé chyby.
Poté, pokud bude chyba ošetřena handlerem, se funkce ukončí. V opačném případě pokračuje vygenerováním chybové hlášky.
Její základ je vygenerován funkcí get_error_text, která používá vygenerované chybové hlášky, viz
samostatný bod dokumentace.
Další zpracování záleží na vážnosti chyby a na tom, zda je definován chybový výstup. Vážnost hlášky
se pozná podle jejího čísla, hlášky dělitelné dvěma jsou závažné chyby, lichá čísla potom patří lehčím
chybám.
Pokud se jedná o lehčí chybu, a je zadefinován chybový výstup různý od standardního, je text hlášky
uložen do něj, v opačném případě se vypíše na chybový výstup.
V konečné fázi je potom v případě vážné chyby ukončen celý program, v opačném případě je funkce
ukončena.
71
9.2. Generování chybových zpráv
V celém projektu jsou z důvodu vícejazykové podpory místo přímých chybových řetězců používány
konstanty. Tyto konstanty jsou potom podle souboru michyba/konstanty.h přeloženy na konkrétní
čísla.
Dále jsou ke každé konstantě definovány v souboru michyba/michybatext.c konkrétní chybové
řetězce pro všechny podporované jazyky, viz sekci o jazykové podpoře. Pro každý podporovaný jazyk je
navíc ve zvláštním souboru definován detailní popis chyby a místo obvyklého výskytu chyby.
Vzhledem k tomu, že tato část kódu je poměrně nová, jsou v ní využity i nové technologie, konkrétně
XSLT, za podpory programu sabcmd z projektu Sablotron. Všechny chyby, tj. jejich čísla, konstanty,
jazykové řetězce i jejich popisy byly v průběhu vývoje ukládány v XML souboru multi.xml, ze kterého
jsou potom všechny výše uvedené soubory pomocí XSLT generovány.
72
9.3. Jazyková podpora
Jak již bylo zmíněno v sekci o generování chyb, je v programu zabudována vícejazyčná podpora.
Konkrétně se jedná o všechny chybové hlášky, a dále detailní popisy chyb, na které se uživatel
dostane z normální chybové hlášky kliknutím na Více.
Pro chybovou hlášku je zvolen jazyk, který je uložen v proměnné AktualniJazyk. Tato proměnná se
dá ovlivnit funkcí LANG, které se jako parametr dá zadefinovat 0 pro češtinu a 1 pro němčinu.
Momentálně jsou podporovány pouze čeština a němčina, plánována je podpora slovenštiny a angličtiny.
73
9.4. Chybové obsluhy
9.4.1. Problematika
Uživatel si smí definovat vlastní chybové obsluhy, které jsou po zachycení chyby spuštěny namísto
chybové hlášky z funkce michyba().
Definované chybové obsluhy, neboli hendlery, jsou ukládány v paměti. Jsou ukládány pomocí parseru
tagu <?MIERROR>. Jsou ukládány v podobě tagu MIVAR nebo tagu MISQL.
V danou chvíli je definován obsluhující hendler, pokud platí jedna z následujících situací:
- globální proměnná MI_ERROR_ACTUAL je nenulová, obsahuje jméno aktuálního hendleru,
- globální proměnná meh_generic je nenulová, obsahuje definici generického hendleru.
Přednost má ve vyhodnocování hendler aktuální před generickým.
Funkce michyba() nastavuje hodnotu nastala_chyba na číslo chyby jen tehdy, byla-li hodnota
proměnné nastala_chyba rovna nule a platí:
- globální proměnná nastala_chyba_detekovat je rovna nule a zároveň je definována v danou
chvíli nějaká chybová obsluha (viz výše)
- globální proměnná nastala_chyba_detekovat je rovna jedné,
Hodnota proměnné nastala_chyba_detekovat je standardně nastavena na hodnotu nula, druhá
možnost platí tedy jen v omezených případech, které jsou ošetřeny zvláště.
V některých tazích lze atributem ERR definovat pojmenovanou chybovou obsluhu pro celé tělo a
hlavičku tagu, v takovém případě je nastavena globální proměnná MI_ERROR_ACTUAL na hodnotu atributu
ERR.
Pokud se v takovém tagu vyskytne chyba a je volána funkce michyba() a je nastavena globální
proměnná nastala_chyba na číslo vyvstalé chyby. Do ní se číslo uloží v jen poprvé, tedy je-li hodnota
proměnné nastala_chyba rovna nule. Obsluhující parsovací funkce si tedy musí kontrolovat, zda proměnná nastala_chyba je stále nulová. Pokud není, znamená to, že je nutno spustit chybovou obsluhu.
Parsovací funkce tagu, která volá ve svém těle rekurzívně jinou parsovací funkcí, která může měnit
obsah proměnné MI_ERROR_ACTUAL si po návratu funkce musí tuto proměnnou nastavit na svoji hodnotu.
9.4.2. Struktura a uložení
Pro uložení informací o definovaném hendleru slouží struktura Tmierror_box, má položky:
- mivar určující, zda jde o hendler typu MIVAR, nebo MISQL, podle typu je hendler odlišně
vykonáván,
- telo obsahuje celé tělo tagu,
- delka určuje délku těla v bytech,
- sqltelo v případě tagu typu MISQL obsahuje SQL výraz, který se vyhodnotí před vykonáním
tagu,
- delsql udává délku SQL výrazu.
Z této struktury je pak hendler vygenerován v případě spuštění.
Dealokaci struktury Tmierror_box provede funkce
74
void dealok_Tmierror_box(Tmierror_box ** box)
Generický chybový hendler je uchován v této podobě v globální proměnnné meh_generic, pojmenované hendlery jsou uloženy v paměti ve stromu meh_koren.
Předpokládá se pouze několik definovaných hendlerů, proto jsou definované hendlery uloženy v AVL
stromu podle jejich jmen.
Vrchol stromu je typu Tmierror_uz a obsahuje položky:
- jmeno - jméno hendleru,
- levy - levý podstrom vrcholu,
- pravy - pravý podstrom vrcholu,
- vyvazenost - vyváženost vrcholu (-1, 0, 1),
- hodnota - vlastní definice hendleru typu Tmierror_box *.
Funkce, která definuje nový uživatelský hendler je
int mierror_set(const char * jmeno,
const char * sqlret,
const unsigned int delsql,
const char * telo,
const unsigned int deltelo)
Její parametry jsou právě jméno nového hendleru, které se použije jako index do AVL stromu
meh_koren. Je-li jméno nulové, jedná se o definici generického hendleru. Ostatní položky definují jednotlivé atributy zmíněné struktury Tmierror_box.
Testování, zda je hendler s daným jménem definován provádí funkce
char isset_errorhendler(char *jmeno)
Celý strom definovaných hendlerů dealokuje funkce
void dealok_Tmierror_Koren()
9.4.3. Spuštění
Hendlery jsou dvou typů: hendler typu MIVAR a typu MISQL. Oba se vyhodnocují ekvivalentně
normálním tagům. Použijí se pro ně tedy stejné parsovací funkce mivar() a misql().
Parsovací funkce berou znaky ze vstupního proudu. Proto se hendler před spuštěním do něj musí
vložit.
Funkce
int mierror_run(char * jmeno, const TParsPozice kde,
int * tag, Tdata_dur * stdven)
podle jména tagu nalezne jeho definici v paměti a vygeneruje tělo hendleru podle jeho typu. V
následující definici jsou použity položky struktury Tmierror_box:
- Pro typ MIVAR se generuje řetězec:”>telo<?/MIVAR>”,
- Pro typ MISQL je generován řetězec:” SQL="sqltelo">telo<?/MISQL>”.
Tento řetězec je vložen na parametrem zadanou pozici ve vstupním proudu. Následně parsován
funkcemi mivar() pro typ MIVAR a misql() pro typ MISQL. Vložení řetězce do vstupního proudu
zajišťuje funkce vloz_filestring(). viz kapitola Bufferování vstupu.
Vygenerování řetězce tagu, jeho vložení, alokování proměnných které jsou k dispozici při vykonávání
tagu, jako i volání parsovací funkce a následné odnastavení proměnných zahrnuje v sobě funkce
75
int mierror_slupka(TParsPozice *kde, Tdata_dur * stdven)
Tato funkce před parsováním tagu nastaví uživatelskou proměnnou $MI_ERRORCODE na číselný kód
chyby. Tento kód je uložen v globální proměnné nastala_chyba. Dále nastavuje řetězec pozice výskytu
chyby do proměnné $MI_ERRORPOS, ten je uložen funkcí michyba() do globální proměnné
nastala_chyba_pozice_buf ve stejném momentě, jako je nastavena hodnota nastala_chyba.
Po ukončení vykonávání hendleru jsou tyto proměnné odnastaveny.
76
10. Databáze
Stejně jako v celém projektu byla i při psaní podpory databází snaha udržet program maximálně
nezávislý a portabilní. Z toho důvodu byl kód, který přímo využívá možností podporované databáze
Informixu vyčleněn pouze do jednoho souboru a to misql/informix.ec.
Zde navíc je v největší možné míře navíc využito Embeded SQL, které by mělo umožňovat snadnou
přenositelnost. Z funkcí přímo závislých na databázi tak zůstalo pouze několik funkcí, které konvertují
data z vnitřních struktur použité databáze.
Dále je Embeded SQL použito i v souboru misql/exp_chk.ec, a tudíž je možné, že při přechodu na
jinou databázi bude třeba pozměnit i tento soubor, protože ne všechny databáze implementují stejnou
množinu Embeded SQL.
Jak již z výše uvedené části vyplynulo, je aktuálně podporovanou databází pouze Informix SE, ovšem
projekt psán tak, aby bylo snadné zabudovat podporu ostatních databází podpoorujících Embeded SQL,
jako je třeba Postgresql či DB2.
77
11. Dynamické řetězce
Dynamické řetězce jsou používány uvnitř kódu na mnoha místech. Slouží k parsování jmen proměnných a funkcí, k postupnému načítání libovolných hodnot po znacích a řetězcích a jejich získání v podobě
souvislého textového řetězce. Předávají se též mnoha funkcím jako parametr, kam se přesměrovává chybový výstup.
Jejich hlavními znaky jsou
- podpora postupného přidávání znaků, řetězců a čísel na konec jejich obsahu,
- vyzdvyhávání hodnoty vložené do dynamického řetězce v několika formátech (souvislý textový
řetězec, číslo),
- optimalizace přizpůsobováním se konkrétnímu použití.
Pozn.: Deklarace struktury je v hlavičkovém souboru mivar/main types.h, deklarace hlavních funkcí
v mivar/prommyhedr.h a definice těl funkcí v mivar/dataistruct.c
78
11.1. Struktura
Dynamický řetězec se skládá z hlavy - struktura Tdata_dur - a z těla: dynamicky alokovaného
spojového seznamu struktur Tdata_dur_ker.
Struktura Tdata_dur obsahuje položky
- Tdata_dur_ker * telo - odkaz na první prvek spojového seznamu,
- Tdata_dur_ker * posledni - odkaz na poslední prvek spojového seznamu, kam se přidávají
nově vkládané hodnoty,
- unsigned int delka - celková délka vloženého obsahu,
- unsigned int pocet - počet vložených řetězců,
- unsigned int odposlchar - počet vložených jednotlivých znaků od posledního přidání slova
nebo čísla.
Struktura Tdata_dur_ker obsahuje položky
- char * data - pole pro obsah,
- Tdata_dur_ker * dalsi - odkaz na další prvek spojového seznamu,
- unsigned int delka - počet bytů alokovaných v položce data,
- unsigned int index - počet bytů již zaplněných v položce data, tedy zároveň index volného
místa v položce data.
79
11.2. Algoritmus přidávání řetězce
Přidávání čísla nebo znaku lze zahrnout pod přidávání textového řetězce. U znaku se jedná o řetězec
délky jedna, číslo se nejprve převede na řetězec.
Při vkládání se nejprve zjistí zda ve spojovém seznamu existuje prvek, který má ještě volné místo
na přidání alespoň jednoho znaku. Může to být jedině prvek, na nějž ukazuje ve struktuře Tdata_dur
položka posledni. Délka volného místa se rovná délce naalokovaného pole data mínus položka index.
Pokud je volné místo, uloží se část, která se vejde, zbytek se uloží v nově alokovaném posledním prvku
spojového seznamu. Na něj se přenastaví položka posledni struktury Tdata_dur.
Otázkou je jak velké pole je třeba alokovat v novém prvku spojového seznamu pro položku data, aby
nebylo zbytečně malé, a zároveň nebylo ani příliš velké a nezabíralo mnoho paměti. Dynamické řetězce lze
použít v mnoha různých situacích v kódu: pro načítání krátkých řetězců i pro opětovné přidávání velkých
řetězců. Dynamický řetězec si z tohoto důvodu vede statistiku o průměrné délce vkládaného slova. A
alokuje vždy trojnásobek tohoto průměru. Je definováno makro udávající počáteční hodnotu, která se do
průměru započítává - je to makro PROMDATADUR_AVGBUFSZ. Minimálně se alokuje PROMDATADUR_MINBUFSZ
bytů.
Za sebou po jednom přidávané znaky (nikoliv tedy po řetězcích, nebo číslech) se považují za součást
jednoho slova, k tomu účelu slouží položka odposlchar struktury Tdata_dur, evidující si kolik znaků
bylo po jednom přidáno od posledního vložení slova nebo čísla. V momentě přidání nového slova nebo
čísla se vezme hodnota v této položce a započítá se do statistiky jako jedno vložené slovo o této délce.
80
11.3. Funkce pro práci s dynamickými řetězci
Inicializaci řetězce na prázdnou hodnotu, případně realokace existujícího řetězce provádí funkce
int init_Tdata_dur(Tdata_dur **p)
Pro alokaci nového řetězce je nutné, aby ukazatel, jehož adresa je předávána jako parametr, byl
nulový, jinak je považován za ukazatel na již existující strukturu.
Přidání znaku a řetězce do dynamického řetězce provedou funkce
int pridej_char_Tdata_dur(Tdata_dur **p, char const r)
int pridej_retezec_Tdata_dur(Tdata_dur **p,
char const *r,
unsigned int delka)
Nebyl-li dynamický řetězec inicializován, musí být hodnota ukazatele p před zavoláním rovna nule.
Do řetězce lze přidat číslo jako long nebo jako UCislo:
int pridej_cislo_Tdata_dur(Tdata_dur **p,
long const cislo)
int pridej_UCislo_Tdata_dur(Tdata_dur **p,
const UCislo cislo,
Tdata_dur * stdven)
Číslo lze vložit i na začátek řetězce:
int vloz_cislo_Tdata_dur(Tdata_dur **p,
long const cislo)
int vloz_UCislo_Tdata_dur(Tdata_dur **p,
const UCislo cislo,
Tdata_dur * stdven);
Pro získání délky řetězce se volá funkce
unsigned int delka_Tdata_dur(Tdata_dur const *p)
Hodnotu jako nově alokovaný řetězec vrací funkce
char * get_retezec_Tdata_dur(Tdata_dur const *p)
Upozornění: hodnotu řetězce vrací bez koncové nuly. Vrací pouze znaky do řetězce vložené.
V číselné podobě hodnotu řetězce vracejí funkce
int vrat_cislo_Tdata_dur(Tdata_dur *p,
long * cislo)
int vrat_UCislo_Tdata_dur(Tdata_dur *p,
UCislo * cislo,
Tdata_dur * stdven)
Dealokaci řetězce provádí funkce
void dealok_Tdata_dur(Tdata_dur **p)
81

Podobné dokumenty

Integer Real

Integer Real co se se stringem dá dělat: s:=’abcdef’+’xyz’; s:=s+’.’; write(s); read(s); length(s) ≈ ord(s[0])

Více

Velmi užitečný text o dlouhých číslech od RNDr. Tomáše Holana.

Velmi užitečný text o dlouhých číslech od RNDr. Tomáše Holana. Proto nám stačı́, když pro výpočet budeme počı́tat a přesnostı́ na 1004 desetinných mı́st. Čı́sla budeme representovat jako čı́sla s pevnou řádovou čárkou (všechna budou mı́t 1004 de...

Více

Pavla Dudková

Pavla Dudková o využití planých rostlin v jídelníčku moderního člověka. Zároveň se zabývám písemnými prameny s tématikou současných archeobotanických výzkumů, z nichž se dá doložit využívání planých rostlin v jí...

Více