\input cwebmac \input csmac % Makra pro češtinu \pageheight=9.5in \fullpageheight=9.8in \setpage %\nocon % omit table of contents \datethis % print date on listing \def\begitems{\medskip\bgroup\catcode`\*=13 \narrower\narrower} \def\enditems{\par\egroup\medskip} {\catcode`\*=13 \gdef*{\par\noindent\llap{$\bullet$\ }\ignorespaces}} \N{1}{1}PROGRAM VLNA. Program čte vstupní textový soubor a nahrazuje za specifikovanými jednopísmennými slovy (např.~v, k, u) mezery symbolem \uv{\.{\char126}}. To zabrání při následném zpracování \TeX{}em zlomit řádek na nevhodných místech, která jsou v rozporu s typografickou normou. Program sestává z těchto hlavních celků: \Y\B\X3:Hlavičkové soubory k načtení\X\6 \X4:Globální deklarace\X\6 \X6:Pomocné funkce\X\6 \X26:Vlnkovací funkce \PB{\\{tie}}\X\6 \X5:Hlavní program\X\par \fi \M{2}Definujeme \PB{\.{BANNER}}, což je text, který se objevi při startu programu a obsahuje číslo verze programu. Zde je názorně vidět, že míchání dvou jazyků se nevyhneme. Při tisku textů na terminál nesmíme předpokládat, že tam budou české fonty. V~této dokumentaci se setkáme se třemi jazyky: angličtinou (většinou v~kódu programu, cestinou v~/* komentářích */ a češtinou jinde. Tu cestinu si vynutil fakt, že DOS-ovská varianta \.{tangle} a \.{weave} se nesnáší s~akcentovanými písmeny v~/* komentářích */. A~nyní už slíbený (vícejazyčný) \PB{\.{BANNER}}. \Y\B\4\D$\.{BANNER}$ \5 \.{"This\ is\ program\ vln}\)\.{a,\ version\ 1.2,\ (c)\ }\)\.{1995,\ 2002\ Petr% \ Olsa}\)\.{k\\n"}\par \fi \M{3}V programu jsou použity knihovní funkce, jejíchž prototypy jsou definovány ve třech standardních hlavičkových souborech. \Y\B\4\X3:Hlavičkové soubory k načtení\X${}\E{}$\6 \8\#\&{include} \.{}\6 \8\#\&{include} \.{}\6 \8\#\&{include} \.{}\par \U1.\fi \M{4}Definujeme konstanty pro návratový kód. \PB{\.{OK}} pro úspěšný běh, \PB{\.{WARNING}} při výskytu aspoň jedné varovné zprávy, \PB{\.{IO\_ERR}} pro chybu v~přístupu ke vtupním nebo výstupním souborům, \PB{\.{BAD\_OPTIONS}} pro syntaktickou chybu na příkazové řádce a \PB{\.{BAD\_PROGRAM}} pro případ havárie programu. Ta by neměla nikdy nastat. Proměnná \PB{\\{status}} bude obsahovat návratový kód a proměnná \PB{\\{prog\_name}} bude ukazovat na text nultého parametru příkazové řádky. \Y\B\4\D$\.{OK}$ \5 \T{0}\par \B\4\D$\.{WARNING}$ \5 \T{1}\par \B\4\D$\.{IO\_ERR}$ \5 \T{2}\par \B\4\D$\.{BAD\_OPTIONS}$ \5 \T{3}\par \B\4\D$\.{BAD\_PROGRAM}$ \5 \T{4}\par \Y\B\4\X4:Globální deklarace\X${}\E{}$\6 \&{char} ${}{*}\\{prog\_name};{}$\6 \&{int} \\{status};\par \As7, 15, 16, 20, 25\ETs33. \U1.\fi \M{5}Základní rozvržení funkce \PB{\\{main}}. \Y\B\4\X5:Hlavní program\X${}\E{}$\6 \&{int} ${}\\{main}(\\{argc},\39\\{argv}){}$\1\1\6 \&{int} \\{argc};\6 \&{char} ${}{*}{*}\\{argv};\2\2{}$\6 ${}\{{}$\1\6 \X9:Lokální proměnné funkce \PB{\\{main}}\X;\6 ${}\\{prog\_name}\K\\{argv}[\T{0}];{}$\6 ${}\\{status}\K\.{OK};{}$\6 \X8:Načtení parametrů příkazového řádku\X;\6 \&{if} ${}(\R\\{silent}){}$\1\5 ${}\\{fprintf}(\\{stderr},\39\.{BANNER});{}$\2\6 \X21:Inicializace datových struktur\X;\6 \X11:Zpracování souborů\X;\6 \&{return} \\{status};\6 \4${}\}{}$\2\par \U1.\fi \N{1}{6}Parametry příkazového řádku. Program čte z~příkazového řádku postupně (nepovinné) parametry, které začínají znakem \uv{\.{-}}. Pak následují jména vstupních a výstupních souborů. \begitems * \.{-f} \dots\ program pracuje jako filtr (viz sekce \PB{$\X11:Zpracování souborů\X$}). Není-li tento parametr použit, program pracuje v tzv. standardním režimu, kdy jednotlivé soubory jsou vstupní i výstupní. * \.{-s} \dots\ program nevypíše \PB{\.{BANNER}}, ani sumarizaci, ani varování, při nichž není program předčasně ukončen. Všechny tyto výpisy směřují do \PB{\\{stderr}}, takže pokud program pracuje v režimu \uv{filtr}, není nutné tento parametr použít. * \.{-r} \dots\ program maže pracovní soubor (soubory), které vytváří ve standardním režimu (tj. není použit \.{-f}). V reřimu filter nemá tento parametr vliv. * \.{-v} \dots\ parametr definuje skupinu písmen, které budou interpretovány jako neslabičné předložky. Např. \.{-v KkSsVvZzOoUuAI}. Pokud není parametr uveden, je použita skupina uvedená v tomto příkladě. * \.{-m} \dots\ program neprovádí kontrolu math/text módů, tj. vlnkuje i uvnitř matematického módu \TeX{}u. (Implicite tam nevlnkuje). * \.{-n} \dots\ prorgram neprovádí kontrolu verbatim módu, tj. vlnkuje i uvnitř verbatim módu definovaném běžnými prostředími. Imlicite ve verbatim prostředí nevlnkuje. * \.{-l} \dots\ La\TeX{} režim. Při kontrole text-math-verbatim módů jsou brány v úvahu další sekvence, obvyklé v La\TeX{}ových dokumentech. * \.{-w} \dots\ WEB režim. Ohraničení verbatim módu je doplněno znaky používanými v dokumentech WEB (např. tento dokument). Důsledek: program vlnkuje dokumentační část každé sekce, ale nikoli kód. \enditems Definujeme funkci \PB{\\{printusage}}, které tiskne (při chybě) stručný přehled možných parametrů. Nepodařilo se mi zjistit, jak se ve WEBu napíše kulturně dlouhý string obsahující \.{\char92n} s formátovacími požadavky. Byl jsem nucen to takto nehezky zapsat. \Y\B\4\X6:Pomocné funkce\X${}\E{}$\6 \&{void} \\{printusage}(\,)\1\1\2\2\6 ${}\{{}$\1\6 ${}\\{fprintf}(\\{stderr},\39\.{"usage:\ vlna\ [opt]\ [}\)\.{filenames]\\n"}% \.{"\ \ opt\ -f\ :\ \ filter\ }\)\.{mode:\ file1\ file2\ ..}\)\.{.\ file1->file2\\n"}\.{"\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ }\)\.{\ \ \ \ \ \ file1\ \ \ \ \ \ \ ..}\)\.{.\ file1->stdout\\n"}\.{"\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ }\)\.{\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ ..}\)\.{.\ stdin->stdout% \\n"}\.{"\ \ \ \ \ \ \ \ \ \ \ \ nofilte}\)\.{r:\ file1\ [file2\ file}\)\.{3\ ...]\ all\ are\ in/ou}\)\.{t\\n"}\.{"\ \ \ \ \ \ -s\ :\ \ silent:}\)\.{\ no\ messages\ to\ stde}\)\.{rr\\n"}\.{"\ \ \ \ \ \ -r\ :\ \ rmbacku}\)\.{p:\ if\ nofilter,\ remo}\)\.{ves\ temporary\ files\\}\)\.{n"}\.{"\ \ \ \ \ \ -v\ charset\ :\ }\)\.{\ set\ of\ lettres\ to\ a}\)\.{dd\ tie,\ default:\ KkS}\)% \.{sVvZzOoUuAI\\n"}\.{"\ \ \ \ \ \ -m\ :\ \ nomath:}\)\.{\ ignores\ math\ modes% \\}\)\.{n"}\.{"\ \ \ \ \ \ -n\ :\ \ noverb:}\)\.{\ ignores\ verbatim\ mo}\)% \.{des\\n"}\.{"\ \ \ \ \ \ -l\ :\ \ LaTeX\ m}\)\.{ode\\n"}\.{"\ \ \ \ \ \ -w\ :% \ \ web\ mod}\)\.{e\\n"});{}$\6 \4${}\}{}$\2\par \As10, 17, 18, 19, 22, 23, 36, 38, 41, 42, 44, 45, 47, 49, 51, 52, 53\ETs55. \U1.\fi \M{7}Proměnné \PB{\\{isfilter}}, \PB{\\{silent}}, \PB{\\{rmbackup}}, \PB{% \\{nomath}}, \PB{\\{noverb}}, \PB{\\{latex}}, resp. \PB{\\{web}} říkají, že je nastaven parametr \.{-f}, % \.{-s}, \.{-r}, \.{-m}, \.{-n}, \.{-l}, resp. \.{-w}. Proměnná \PB{\\{charset}} ukazuje buď na implicitní skupinu znaků \PB{\\{charsetdefault}}, nebo (při použití parametru \.{-v}) na text uvedený v příkazovém řádku. \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{int} \\{isfilter}${}\K\T{0},{}$ \\{silent}${}\K\T{0},{}$ \\{rmbackup}${}\K% \T{0},{}$ \\{nomath}${}\K\T{0},{}$ \\{noverb}${}\K\T{0},{}$ \\{web}${}\K% \T{0},{}$ \\{latex}${}\K\T{0};{}$\6 \&{char} \\{charsetdefault}[\,]${}\K\.{"KkSsVvZzOoUuAI"};{}$\6 \&{char} ${}{*}\\{charset}\K\\{charsetdefault}{}$;\par \fi \M{8}\B\X8:Načtení parametrů příkazového řádku\X${}\E{}$\6 \&{while} ${}(\\{argc}>\T{1}\W\\{argv}[\T{1}][\T{0}]\E\.{'-'}){}$\5 ${}\{{}$\1\6 \&{if} ${}(\\{argv}[\T{1}][\T{2}]\I\T{0}){}$\1\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\2\6 \&{switch} (\\{argv}[\T{1}][\T{1}])\5 ${}\{{}$\1\6 \4\&{case} \.{'f'}:\5 ${}\\{isfilter}\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'s'}:\5 ${}\\{silent}\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'r'}:\5 ${}\\{rmbackup}\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'v'}:\6 \&{if} ${}(\\{argc}<\T{2}){}$\1\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\2\6 ${}\\{argv}\PP;{}$\6 ${}\\{argc}\MM;{}$\6 ${}\\{charset}\K\\{argv}[\T{1}];{}$\6 \&{break};\6 \4\&{case} \.{'m'}:\5 ${}\\{nomath}\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'n'}:\5 ${}\\{noverb}\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'l'}:\5 ${}\\{latex}\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'w'}:\5 ${}\\{web}\K\T{1};{}$\6 \&{break};\6 \4\&{default}:\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS}){}$;\C{ nezn\'am\'y parametr }\6 \4${}\}{}$\2\6 ${}\\{argc}\MM;{}$\6 ${}\\{argv}\PP;{}$\6 \4${}\}{}$\2\par \U5.\fi \N{1}{9}Zpracování souborů. Parametr \PB{\.{MAXLEN}} definuje maximální možnou délku jména souboru, který vytvoříme jako přechodný, nebo zálohový. Dále deklarujeme proměnné typu \uv{stream}. \Y\B\4\D$\.{MAXLEN}$ \5 \T{120}\par \Y\B\4\X9:Lokální proměnné funkce \PB{\\{main}}\X${}\E{}$\6 \&{FILE} ${}{*}\\{infile},{}$ ${}{*}\\{outfile};{}$\6 \&{char} \\{backup}[\.{MAXLEN}];\6 \&{int} \|j;\par \U5.\fi \M{10}Definujeme funkci pro výpis chybového hlášení při neúspěšném otevření souboru. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{ioerr}(\|f)\1\1\6 \&{char} ${}{*}\|f;\2\2{}$\6 ${}\{{}$\1\6 ${}\\{fprintf}(\\{stderr},\39\.{"\%s:\ cannot\ open\ fil}\)\.{e\ \%s\\n"},\39% \\{prog\_name},\39\|f);{}$\6 \4${}\}{}$\2\par \fi \M{11}Způsob zpracování souborů rozlišíme podle režimu daným přepínačem \.{-f}. \Y\B\4\X11:Zpracování souborů\X${}\E{}$\6 \&{if} (\\{isfilter}) \X12:Zpracování v režimu filter\X\6 \&{else} \X13:Zpracování všech souborů příkazové řádky\X\par \Q6. \U5.\fi \M{12}V režimu \PB{$\\{isfilter}\E\T{1}$} je další zpracování závislé na počtu souborů v příkazové řádce: \begitems * nula souborů -- vstup je \PB{\\{stdin}} a výstup je \PB{\\{stdout}}, * jeden soubor -- je vstupní, výstup je \PB{\\{stdout}}, * dva soubory -- první je vstupní, druhý výstupní, * více souborů -- program skončí s chybou. \enditems \Y\B\4\X12:Zpracování v režimu filter\X${}\E{}$\6 ${}\{{}$\1\6 \&{if} ${}(\\{argc}>\T{3}){}$\1\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\2\6 ${}\\{infile}\K\\{stdin};{}$\6 ${}\\{outfile}\K\\{stdout};{}$\6 \&{if} ${}(\\{argc}\G\T{2}){}$\1\5 ${}\\{infile}\K\\{fopen}(\\{argv}[\T{1}],\39\.{"r"});{}$\2\6 \&{if} ${}(\\{infile}\E\NULL){}$\1\5 ${}\\{ioerr}(\\{argv}[\T{1}]),\39\\{exit}(\.{IO\_ERR});{}$\2\6 \&{if} ${}(\\{argc}\E\T{3}){}$\1\5 ${}\\{outfile}\K\\{fopen}(\\{argv}[\T{2}],\39\.{"w"});{}$\2\6 \&{if} ${}(\\{outfile}\E\NULL){}$\1\5 ${}\\{ioerr}(\\{argv}[\T{2}]),\39\\{exit}(\.{IO\_ERR});{}$\2\6 \&{if} ${}(\\{argc}\G\T{2}){}$\1\5 ${}\\{filename}\K\\{argv}[\T{1}];{}$\2\6 \&{else}\1\5 ${}\\{filename}\K\NULL;{}$\2\6 ${}\\{tie}(\\{infile},\39\\{outfile});{}$\6 \&{if} ${}(\\{outfile}\I\\{stdout}){}$\1\5 \\{fclose}(\\{outfile});\2\6 \&{if} ${}(\\{infile}\I\\{stdin}){}$\1\5 \\{fclose}(\\{infile});\2\6 \4${}\}{}$\2\par \U11.\fi \M{13}V~režimu \PB{$\\{isfilter}\E\T{0}$} jsou jednotlivé soubory v~příkazovém řádku interpretovány jako vstupní i výstupní. Více souborů v~příkazovém řádku má stejný efekt, jako opakované volání programu na jednotlivé soubory. V~\UNIX/u lze tedy např. napsat \.{\jobname\ *.tex} a program doplní vlnky do všech souborů s~příponou~\.{tex}. Toto neplatí v~DOSu, protože interpretace masky je v~\UNIX/u starostí shellu a nikoli programu samotného. Náš program masku nebude interpretovat. Je-li v~tomto režimu nulový počet souborů, program se ukončí s~chybou. \Y\B\4\X13:Zpracování všech souborů příkazové řádky\X${}\E{}$\6 ${}\{{}$\1\6 \&{if} ${}(\\{argc}\E\T{1}){}$\1\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\2\6 \&{while} ${}(\\{argc}>\T{1}){}$\5 ${}\{{}$\1\6 ${}\\{argc}\MM;{}$\6 ${}\\{argv}\PP;{}$\6 \X14:Přejmenuj vstup \PB{\\{argv}[\T{0}]} na \PB{\\{backup}} a otevři jej jako % \PB{\\{infile}}\X;\6 \&{if} ${}(\\{infile}\E\NULL){}$\5 ${}\{{}$\1\6 \\{ioerr}(\\{argv}[\T{0}]);\6 \&{continue};\6 \4${}\}{}$\2\6 ${}\\{outfile}\K\\{fopen}(\\{argv}[\T{0}],\39\.{"w"});{}$\6 \&{if} ${}(\\{outfile}\E\NULL){}$\5 ${}\{{}$\1\6 \\{ioerr}(\\{argv}[\T{0}]);\6 ${}\\{rename}(\\{backup},\39\\{argv}[\T{0}]);{}$\6 ${}\\{status}\K\.{WARNING};{}$\6 \&{continue};\6 \4${}\}{}$\2\6 ${}\\{filename}\K\\{argv}[\T{0}];{}$\6 ${}\\{tie}(\\{infile},\39\\{outfile});{}$\6 ${}\\{fclose}(\\{outfile}),\39\\{fclose}(\\{infile});{}$\6 \&{if} (\\{rmbackup})\1\5 \\{remove}(\\{backup});\2\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \U11.\fi \M{14}Při \PB{$\\{isfilter}\E\T{0}$} program přejmenuje každý zpracovávaný soubor tak, že změní poslední písmeno názvu souboru na znak \.{\char126}. Tento přejmenovaný soubor bude otevřen jako vstupní a výstupem bude původní soubor. Vstupní soubor při \PB{$\\{rmbackup}\E\T{0}$} zůstane zachován jako záloha. Proč vlnku nepřidáváme na konec názvu souboru, ale měníme ji za poslední znak souboru? Protože chceme, aby program fungoval i v tak nemožných systémech, jako je DOS. \Y\B\4\X14:Přejmenuj vstup \PB{\\{argv}[\T{0}]} na \PB{\\{backup}} a otevři jej jako \PB{\\{infile}}\X${}\E{}$\6 $\\{infile}\K\NULL;{}$\6 ${}\|j\K\\{strlen}(\\{argv}[\T{0}])-\T{1};{}$\6 \&{if} ${}(\|j\G\.{MAXLEN}\V\\{argv}[\T{0}][\|j]\E\.{'\~'}){}$\5 ${}\{{}$\1\6 \&{if} ${}(\R\\{silent}){}$\1\5 ${}\\{fprintf}(\\{stderr},\39\.{"\%s:\ the\ conflict\ of}\)\.{\ file\ name\ \%s% \\n"},\39\\{prog\_name},\39\\{argv}[\T{0}]);{}$\2\6 \4${}\}{}$\2\6 \&{else}\5 ${}\{{}$\1\6 ${}\\{strcpy}(\\{backup},\39\\{argv}[\T{0}]);{}$\6 ${}\\{backup}[\|j]\K\.{'\~'};{}$\6 \\{remove}(\\{backup});\6 ${}\|j\K\\{rename}(\\{argv}[\T{0}],\39\\{backup});{}$\6 \&{if} ${}(\|j\E\T{0}){}$\1\5 ${}\\{infile}\K\\{fopen}(\\{backup},\39\.{"r"});{}$\2\6 \4${}\}{}$\2\par \U13.\fi \N{1}{15}Patterny. Abychom mohli účelně definovat chování programu v~různých situacích, zavedeme datovou strukturu \PB{\.{PATTERN}}. Zhruba řečeno, budeme sledovat vstup znak po znaku a pokud bude část vstupu souhlasit s~definovaným patternem, provedeme námi požadovanou akci. Například nejčastější aktivitu, přidání vlnky uvnitř řádku, spustíme v~okamžiku, kdy vstupní text odpovídá patternu \uv{\.{\ (v\ p}}, kde \uv{\.{\ }} znamená jedna nebo více mezer a tabelátorů, \uv{\.{(}} je nula nebo více otevíracích závorek všeho druhu, \uv{\.{v}} znamená jedno písmeno z~množiny předložek (viz \PB{\\{charset}}) a \uv{\.{p}} zde znamená libovolné písmeno. Příklad zde není zcela přesný. Přesně jsou všechny patterny pro náš program definovány v~závěrečných sekcích tohoto povídání. Pattern bude znamenat konečnou sekvenci tzv. pozic patternu (\PB{\.{PATITEM}}). Cykly uvnitř pozic pro jednoduchost nepřipustíme. Každá pozice obsahuje řetězec znaků, uvažovaný pro danou pozici (v~příkladu pozice~\uv{\.{\ }} by obsahovala mezeru a tabelátor, zatímco pozice \.{v} odpovídá \PB{\\{charset}}). Každá pozice má svůj přepínač (\PB{\\{flag}}), který obsahuje informaci o~tom, zda shodu testovaného znaku s~některým prvkem v~množině znaků budeme považovat za úspěch či neúspěch a zda pozice se ve zkoumaném řetězci může vyskytovat právě jednou nebo opakovaně. Jako druhý případ stačí implementovat \uv{nula nebo více} protože \uv{jedna nebo více} lze popsat pomocí dvou pozic, první \uv{právě jednou} a následující \uv{nula nebo více}. Jednotlivé pozice jsou zřetězeny ukazatelem \PB{\\{next}}, poslední pozice má \PB{$\\{next}\E\NULL$}. Stejně tak jednotlivé patterny budeme sestavovat do seznamů a budou rovněž zřetězeny ukazatelem \PB{\\{next}}. Pattern kromě řetězu pozic obsahuje ukazatel na funkci (proceduru) \PB{% \\{proc}}, která se má vykonat v~případě, že testovaný řetězec vyhovuje patternu. \Y\B\4\D$\.{ONE}$ \5 \T{1}\C{ flag: prave jeden vyskyt }\par \B\4\D$\.{ANY}$ \5 \T{2}\C{ flag: nula nebo vice }\par \B\4\D$\.{ONE\_NOT}$ \5 ${-}{}$\T{1}\C{ flag: prave jednou, znak nesmi byt v mnozine }\par \B\4\D$\.{ANY\_NOT}$ \5 ${-}{}$\T{2}\C{ flag: nula nebo vice, znak nesmi byt v mnozine }\par \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{typedef} \&{struct} \&{PATITEM} ${}\{{}$\C{ jedna pozice patternu }\1\6 \&{char} ${}{*}\\{str}{}$;\C{ seznam znaku na teto pozici }\6 \&{int} \\{flag};\C{ vyznam seznamu znaku }\6 \&{struct} \&{PATITEM} ${}{*}\\{next}{}$;\C{ nasledujici pozice patternu }\2\6 ${}\}{}$ \&{PATITEM};\6 \&{typedef} \&{struct} \&{PATTERN} ${}\{{}$\C{ jeden pattern }\1\6 \&{PATITEM} ${}{*}\\{patt}{}$;\C{ ukazatel na prvni pozici }\7 ${}\&{void}({*}\\{proc})(\,){}$;\C{ procedura spustena pri souhlasu patternu }\7 \&{struct} \&{PATTERN} ${}{*}\\{next}{}$;\C{ nasledujici v seznamu vsech patternu }\2\6 ${}\}{}$ \&{PATTERN};\par \fi \M{16}Deklarujeme některé globální proměnné pro práci s~patterny. \PB{\\{lapi}} je pole obsahující ukazatele na aktuální pozice v~otevřených patternech. Říkáme, že \uv{pattern je otevřen}, pokud zkoumaný řetězec s~ním {\it začíná\/} souhlasit. Pattern se uzavře, pokud nastane jedna ze dvou možností: zkoumaný řetězec s~mím souhlasí až do konce (v~takovém případě se provede procedura \PB{\\{proc}}), nebo při vyšetřování dalších znaků ze zkoumaného řetězce přestane řetězec s~patternem souhlasit. V~dané chvíli může být pattern otevřen několikrát. Např. pattern \.{abac} je při stringu \.{aba} při výskytu druhého \.{a} otevřen podruhé. Proto pole obsahuje ukazatele na právě aktuální pozici patternu a nikoli na pattern jako takový. V~poli \PB{\\{lapi}} budou na počátku samá \PB{$\NULL$} (to se při překladu inicializuje samo) a přemazání ukazatele na pozici konstantou \PB{$\NULL$} budeme považovat za zavření patternu. Vedle pole \PB{\\{lapi}} souměrně udržujeme pole \PB{% \\{lapt}}, do něhož budeme ukládat ukazatele na odpovídající otevřený pattern. Tuto informaci použijeme v~případě, že potřebujeme např, znát \PB{\\{proc}} patternu. \PB{\\{listpatt}} bude ukazovat na začátek aktuálního seznamu patternů. Seznamy budeme mít dva. Jeden se použije, nacházíme-li se mimo komentář a druhý v~případě, že se nacházíme v~prostoru \TeX{}ovského komentáře (tj. za procentem). Starty těchto seznamů patternů jsou \PB{\\{normallist}} a \PB{\\{commentlist}} a aktivní \PB{\\{listpatt}} má vždy jednu z~těchto dvou hodnot. Proměnné \PB{\\{lastpt}} a \PB{\\{lastpi}} použijeme pro budování řetězové struktury patternů. Proměnná \PB{\|c} obsahuje právě testovaný znak ze vstupu (který se rovněž přepíše do bufferu \PB{\\{buff}}). Z~bufferu občas ukládáme data do výstupního proudu. Děláme to ale vždy jen v~okamžiku, kdy není otevřen žádný pattern. Tehdy totiž \uv{nehrozí} situace, že by nějaká procedura vyvolaná souhlasem patternu požadovala v~tomto bufferu nějaké změny se zpětnou platností. O~vyprázdnění bufferu se začneme zajímat až v~okamžiku, kdy je zaplněn aspoň na hodnotu \PB{\.{BUFI}}, abychom proceduru přepisu bufferu do výstupního proudu neaktivovali zbytečně často. \Y\B\4\D$\.{MAXPATT}$ \5 \T{200}\C{ maximalni pocet patternu }\par \B\4\D$\.{MAXBUFF}$ \5 \T{500}\C{ velikost bufferu pro operace }\par \B\4\D$\.{BUFI}$ \5 \T{300}\C{ velikost stredniho zaplneni }\par \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{PATITEM} ${}{*}\\{lapi}[\.{MAXPATT}]{}$;\C{ pole ukazatelu na aktualni pozice }\6 \&{PATTERN} ${}{*}\\{lapt}[\.{MAXPATT}]{}$;\C{ pole odpovidajicich ukazatelu na patterny }\6 \&{PATTERN} ${}{*}\\{listpatt},{}$ ${}{*}\\{normallist},{}$ ${}{*}% \\{commentlist},{}$ ${}{*}\\{pt},{}$ ${}{*}\\{lastpt}\K\NULL;{}$\6 \&{PATITEM} ${}{*}\\{pi},{}$ ${}{*}\\{lastpi}\K\NULL;{}$\6 \&{char} \|c;\C{ zrovna nacetny znak }\6 \&{char} \\{buff}[\.{MAXBUFF}];\C{ prechodny buffer }\6 \&{int} \\{ind};\C{ aktualni pozice prechodneho bufferu }\par \fi \M{17}Nyní definujeme pomocné funkce \PB{\\{setpattern}}, \PB{\\{setpi}} a \PB{% \\{normalpattern}}. Tyto funkce alokují paměť pomocí standardní funkce \PB{\\{malloc}}. Abychom mohli ohlídat případnou chybu při alokaci, budeme allokovat paměť zprostředkovaně pomocí funkce \PB{\\{myalloc}}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} ${}{*}\\{myalloc}(\\{size}){}$\1\1\6 \&{int} \\{size};\2\2\6 ${}\{{}$\1\6 \&{void} ${}{*}\|p;{}$\7 ${}\|p\K\\{malloc}(\\{size});{}$\6 \&{if} ${}(\|p\E\NULL){}$\5 ${}\{{}$\1\6 ${}\\{fprintf}(\\{stderr},\39\.{"\%s,\ no\ memory,\ mall}\)\.{oc\ failed\\n"},% \39\\{prog\_name});{}$\6 \\{exit}(\.{BAD\_PROGRAM});\6 \4${}\}{}$\2\6 \&{return} \|p;\6 \4${}\}{}$\2\par \fi \M{18}Funkce \PB{\\{setpattern}} alokuje paměťové místo struktury \PB{% \&{PATTERN}} a napojí ji pomocí proměnné \PB{\\{lastpt}} na už alokovaný řetěz patternů. Vrátí ukazatel na nově alokované místo. Jednotlivé pozice patternu se musí následovně alokovat pomocí \PB{\\{setpi}}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{PATTERN} ${}{*}{}$\\{setpattern}(\\{proc})\6 ${}\&{void}({*}\\{proc})(\,);{}$\7 ${}\{{}$\1\6 \&{PATTERN} ${}{*}\\{pp};{}$\7 ${}\\{pp}\K\\{myalloc}(\&{sizeof}(\&{PATTERN}));{}$\6 ${}\\{pp}\MG\\{proc}\K\\{proc};{}$\6 ${}\\{pp}\MG\\{next}\K\NULL;{}$\6 ${}\\{pp}\MG\\{patt}\K\NULL;{}$\6 \&{if} ${}(\\{lastpt}\I\NULL){}$\1\5 ${}\\{lastpt}\MG\\{next}\K\\{pp};{}$\2\6 ${}\\{lastpt}\K\\{pp};{}$\6 ${}\\{lastpi}\K\NULL;{}$\6 \&{return} \\{pp};\6 \4${}\}{}$\2\par \fi \M{19}Funkce \PB{\\{setpi}} alokuje paměťové místo pro jednu pozici patternu. Provede zřetězení tak, aby první pozice řetězu pozic byla zaznamenána v položce \PB{\\{patt}} ve struktuře \PB{\&{PATTERN}} a další byly provázány položkou % \PB{\\{next}} ve struktuře \PB{\&{PATITEM}}. Poslední pozice má \PB{$\\{next}\E\NULL$}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} ${}\\{setpi}(\\{str},\39\\{flag}){}$\1\1\6 \&{char} ${}{*}\\{str};{}$\6 \&{int} \\{flag};\2\2\6 ${}\{{}$\1\6 \&{PATITEM} ${}{*}\|p;{}$\7 ${}\|p\K\\{myalloc}(\&{sizeof}(\&{PATITEM}));{}$\6 ${}\|p\MG\\{str}\K\\{str};{}$\6 ${}\|p\MG\\{flag}\K\\{flag};{}$\6 ${}\|p\MG\\{next}\K\NULL;{}$\6 \&{if} ${}(\\{lastpi}\E\NULL){}$\1\5 ${}\\{lastpt}\MG\\{patt}\K\|p;{}$\2\6 \&{else}\1\5 ${}\\{lastpi}\MG\\{next}\K\|p;{}$\2\6 ${}\\{lastpi}\K\|p;{}$\6 \4${}\}{}$\2\par \fi \M{20}Připravme si půdu pro funkci \PB{\\{normalpattern}}. Tato funkce alokuje strukturu pro jeden pattern včetně pozic patternu na základě vstupního stringu. Každá pozice patternu obsahuje v~množině znaků jediný znak a má \PB{$\\{flag}\K\.{ONE}$}. Znaky ve vstupním stringu odpovídají po řadě jednotlivým pozicím. Vytvoří se vlastně jakýsi absolutní pattern, tj. testovaný řetězec se musí přesně shodovat s~uvedeným stringem. Výjimku tvoří znak \PB{\.{"."}}, který se interpretuje jako nula nebo více mezer. Chceme-li tečku vnutit do patternu, napíšeme dvě tečky za sebou. Nejdříve deklarujeme pole všech možných jednopísmenných stringů. \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{char} \\{strings}[\T{512}];\6 \&{int} \|i;\par \fi \M{21}Inicializujeme toto pole (znak, nula, znak, nula, atd...). \Y\B\4\X21:Inicializace datových struktur\X${}\E{}$\6 \&{for} ${}(\|i\K\T{0};{}$ ${}\|i<\T{256};{}$ ${}\|i\PP){}$\5 ${}\{{}$\1\6 ${}\\{strings}[\T{2}*\|i]\K{}$(\&{char}) \|i;\6 ${}\\{strings}[\T{2}*\|i+\T{1}]\K\T{0};{}$\6 \4${}\}{}$\2\par \As34, 37, 39, 40, 43, 46, 48, 50\ETs54. \U5.\fi \M{22}Definujme funkci \PB{\\{normalpattern}}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{PATTERN} ${}{*}\\{normalpattern}(\\{proc},\39\\{str}{}$)\6 ${}\&{void}({*}\\{proc})(\,);{}$\6 \&{char} ${}{*}\\{str};{}$\7 ${}\{{}$\1\6 \&{PATTERN} ${}{*}\\{pp};{}$\6 \&{int} \|j${}\K\T{0};{}$\7 ${}\\{pp}\K\\{setpattern}(\\{proc});{}$\6 \&{while} (\\{str}[\|j])\5 ${}\{{}$\1\6 \&{if} ${}(\\{str}[\|j]\E\.{'.'}){}$\5 ${}\{{}$\1\6 ${}\|j\PP;{}$\6 \&{if} ${}(\\{str}[\|j]\I\.{'.'}){}$\5 ${}\{{}$\1\6 ${}\\{setpi}(\\{blankscr},\39\.{ANY});{}$\6 \&{continue};\6 \4${}\}{}$\2\6 \4${}\}{}$\2\6 ${}\\{setpi}({\AND}\\{strings}{}$[(\&{unsigned} \&{char}) \\{str}[\|j]${}*% \T{2}],\39\.{ONE});{}$\6 ${}\|j\PP;{}$\6 \4${}\}{}$\2\6 \&{return} \\{pp};\6 \4${}\}{}$\2\par \fi \M{23}Funkce \PB{\\{match}}. Definujeme funkci, která na základě hodnoty znaku % \PB{\|c} (proměnná \PB{\|c} je definována jako globální), a pozice patternu \PB{\|p} (parametr funkce) vrátí informaci o tom, zda znak souhlasí s patternem. Záporná čísla \PB{\.{FOUND}}, resp. \PB{\.{NOFOUND}} znamenají, že je třeba uzavřít pattern s tím, že vzor odpovídá, resp. neodpovídá patternu. Nezáporné číslo vrátí v případě, že zkoumaný vstup stále souhlasí s patternem, ale není ještě rozhodnuto. Velikost návratové hodnoty v takovém případě udává, o kolik pozic je třeba se posunout v patternu, abychom měli ukazatel na pozici patternu v souhlase s novou situací, způsobenou znakem \PB{\|c}. Pokud je \PB{\|c} v množině znaků pro danou pozici \PB{$\|p\MG\\{str}$}, bude % \PB{$\|m\E\T{1}$}, jinak je \PB{$\|m\E{-}\T{1}$}. Pokud tímto číslem pronásobíme hodnotu \PB{$\|p\MG% \\{flag}$}, nemusíme větvení podle \PB{$\|p\MG\\{flag}$} programovat dvakrát. Hodnoty \PB{\\{flag}} jsou totiž symetrické podle nuly, např. \PB{$\.{ANY}\E{-}\.{ANY\_NOT}$}. \Y\B\4\D$\.{FOUND}$ \5 ${-}{}$\T{1}\par \B\4\D$\.{NOFOUND}$ \5 ${-}{}$\T{2}\par \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{int} \\{match}(\|p)\1\1\6 \&{PATITEM} ${}{*}\|p;\2\2{}$\6 ${}\{{}$\1\6 \&{int} \|m;\7 \&{if} ${}(\\{strchr}(\|p\MG\\{str},\39\|c)\I\NULL){}$\1\5 ${}\|m\K\T{1}{}$;\C{ Znak nalezen }\2\6 \&{else}\1\5 ${}\|m\K{-}\T{1}{}$;\C{ Znak nenalezen }\2\6 \&{switch} ${}(\|m*\|p\MG\\{flag}){}$\5 ${}\{{}$\1\6 \4\&{case} \.{ANY}:\5 \&{return} \T{0};\C{ Souhas, neni nutny posun }\6 \4\&{case} \.{ONE}:\6 \&{if} ${}(\|p\MG\\{next}\E\NULL){}$\1\5 \&{return} \.{FOUND};\2\6 \&{return} \T{1};\C{ Souhas, nutny posun o 1 }\6 \4\&{case} \.{ONE\_NOT}:\5 \&{return} \.{NOFOUND};\C{ Nesouhlas }\6 \4\&{case} \.{ANY\_NOT}:\5 \X24:Vrať hodnotu podle následující pozice patternu\X;\6 \4${}\}{}$\2\6 \&{return} \T{0};\C{ Tady bychom nikdy nemeli byt, return pro potlaceni varovani }\6 \4${}\}{}$\2\par \fi \M{24}O kolik pozic je třeba se posunout a s jakým výsledkem zjistíme rekurzivním voláním funkce \PB{\\{match}}. \Y\B\4\X24:Vrať hodnotu podle následující pozice patternu\X${}\E{}$\6 \&{switch} ${}(\|m\K\\{match}(\|p\MG\\{next})){}$\5 ${}\{{}$\1\6 \4\&{case} \.{NOFOUND}:\5 \&{return} \.{NOFOUND};\6 \4\&{case} \.{FOUND}:\5 \&{return} \.{FOUND};\6 \4\&{default}:\5 \&{return} \T{1}${}+\|m;{}$\6 \4${}\}{}$\2\par \U23.\fi \N{1}{25}Vlnkovací funkce. Nejprve připravíme globální deklarace pro \uv{vlnkovací} funkci \PB{\\{tie}}. Funkce \PB{\\{tie}} \uv{ovlnkuje} vstupní soubor \PB{\\{infile}} a vytvoří soubor \PB{\\{outfile}}. Při \PB{$\\{silent}\K\T{0}$} tiskne závěrečnou zprávu o zpracování. V této zprávě se objeví jméno souboru, které se funkce \uv{dozví} prostřednictvím globální proměnné \PB{\\{filename}}. Proměnná \PB{\\{numline}} počítá řádky, proměnná \PB{\\{numchanges}} sčítá změny, tj. počet doplněných vlnek. Proměnná \PB{\\{mode}} nabývý některé z hodnot \PB{\.{TEXTMODE}}, \PB{% \.{MATHMODE}}, \PB{\.{DISPLAYMODE}} a \PB{\.{VERBMODE}} podle stavu ve čteném textu. \Y\B\4\D$\.{TEXTMODE}$ \5 \T{0}\par \B\4\D$\.{MATHMODE}$ \5 \T{1}\par \B\4\D$\.{DISPLAYMODE}$ \5 \T{2}\par \B\4\D$\.{VERBMODE}$ \5 \T{3}\par \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{char} ${}{*}\\{filename}{}$;\C{ jmeno zpracovavaneho souboru }\6 \&{long} \&{int} \\{numline}${},{}$ \\{numchanges};\C{ pro zaverecnou statistiku }\6 \&{int} \\{mode};\par \fi \M{26}Nyní definujeme vlnkovací funkci \PB{\\{tie}}. Veškerá činnost se opírá o strukturu patternů. Výhodné je (z důvodu rychlosti) \uv{natvrdo} zde implementovat jen přepínání mezi stavem čtení z oblasti komentáře (\PB{$\\{listpatt}\E\\{commentlist}$}) a mimo komentář (\PB{$\\{listpatt}\E% \\{normallist}$}); \Y\B\4\X26:Vlnkovací funkce \PB{\\{tie}}\X${}\E{}$\6 \&{void} ${}\\{tie}(\\{input},\39\\{output}){}$\1\1\6 \&{FILE} ${}{*}\\{input},{}$ ${}{*}\\{output};\2\2{}$\6 ${}\{{}$\1\6 \&{int} \\{ap};\C{ ap je pocet otevrenych patternu }\6 \&{register} \&{int} \|k${},{}$ \|m${},{}$ \|n;\6 \&{int} \\{ic};\6 \&{PATTERN} ${}{*}\\{pp};{}$\6 \&{PATITEM} ${}{*}\\{pi};{}$\7 \X27:Inicializace proměnných při startu funkce \PB{\\{tie}}\X;\6 \&{while} ${}(\R\\{feof}(\\{input})){}$\5 ${}\{{}$\1\6 \X30:Otevři nové patterny\X;\6 \&{if} ${}(\\{ap}\E\T{0}\W\\{ind}>\.{BUFI}\W\|c\I\.{'\\\\'}){}$\1\5 \X28:Vyprázdni buffer\X;\2\6 \&{if} ${}(\\{ind}\G\.{MAXBUFF}){}$\5 ${}\{{}$\1\6 ${}\\{fprintf}(\\{stderr},\39\.{"Operating\ buffer\ ov}\)\.{erflow,\ is\ anything\ }\)\.{wrong?\\n"});{}$\6 \\{exit}(\.{BAD\_PROGRAM});\6 \4${}\}{}$\2\6 \&{if} ${}((\\{ic}\K\\{getc}(\\{input}))\E\.{EOF}{}$)\C{ opravil Cejka Rudolf }% \1\6 \&{break};\2\6 ${}\\{buff}[\\{ind}\PP]\K\|c\K\\{ic};{}$\6 \&{if} ${}(\|c\E\.{'\\n'}){}$\1\5 ${}\\{numline}\PP,\39\\{listpatt}\K\\{normallist};{}$\2\6 \&{if} ${}(\|c\E\.{'\%'}\W\\{mode}\I\.{VERBMODE}\W\\{buff}[\\{ind}-\T{2}]\I\.{'% \\\\'}){}$\1\5 ${}\\{listpatt}\K\\{commentlist};{}$\2\6 \X29:Projdi otevřené patterny\X;\6 \4${}\}{}$\2\6 \X28:Vyprázdni buffer\X;\6 \&{if} ${}(\R\\{web}){}$\1\5 \\{checkmode}(\,);\C{ zaverecna kontrola modu }\2\6 \&{if} ${}(\R\\{silent}){}$\1\5 \X32:Tiskni závěrečnou zprávu\X;\2\6 \4${}\}{}$\2\par \U1.\fi \M{27}\B\X27:Inicializace proměnných při startu funkce \PB{\\{tie}}\X${}\E{}$\6 \&{for} ${}(\|k\K\T{0};{}$ ${}\|k<\.{MAXPATT};{}$ ${}\|k\PP){}$\1\5 ${}\\{lapi}[\|k]\K\NULL;{}$\2\6 ${}\|c\K\.{'\\n'};{}$\6 ${}\\{buff}[\T{0}]\K\\{mode}\K\\{ap}\K\T{0};{}$\6 ${}\\{ind}\K\T{1};{}$\6 ${}\\{numline}\K\T{1};{}$\6 ${}\\{numchanges}\K\T{0};{}$\6 ${}\\{mode}\K\.{TEXTMODE}{}$;\par \A35. \U26.\fi \M{28}Při manipulaci s bufferem byl použit jeden trik. Veškeré načtené znaky začínají až od \PB{\\{buff}[\T{1}]}, zatímco \PB{\\{buff}[\T{0}]} je rovno nule. Je to proto, že některé algoritmy se vrací o jeden znak zpět za svůj pattern, aby zjistily, zda tam není symbol \uv{\.{\char92}} (například na výskyt sekvence \.{\char92\char37} je třeba reagovat jinak, než na výskyt obyčejného procenta). Kdybychm zazačali od \PB{\\{buff}[\T{0}]}, v některých situacích bychom se ptali, zda \PB{$\\{buff}[{-}\T{1}]\E\.{'\\\\'}$}, tj. sahali bychom na neošetřené místo v paměti. \Y\B\4\X28:Vyprázdni buffer\X${}\E{}$\6 ${}\{{}$\1\6 ${}\\{buff}[\\{ind}]\K\T{0};{}$\6 ${}\\{fputs}({\AND}\\{buff}[\T{1}],\39\\{output});{}$\6 ${}\\{ind}\K\T{1};{}$\6 \4${}\}{}$\2\par \U26.\fi \M{29}Při procházení otevřenými patterny posunujeme v poli \PB{\\{lapi}} pozice jednotlivých patternů podle pokynů funkce \PB{\\{match}}, případně pattern zavřeme a případně vyvoláme proceduru patternu. Některé patterny v poli \PB{\\{lapi}} už mohou být zavřeny, takže je nutno s tímto polem pracovat jako s jakýmsi děravým sýrem. \Y\B\4\X29:Projdi otevřené patterny\X${}\E{}$\6 $\|n\K\\{ap};{}$\6 ${}\|k\K\T{0};{}$\6 \&{while} (\|n)\5 ${}\{{}$\1\6 \&{while} ${}(\\{lapi}[\|k]\E\NULL){}$\1\5 ${}\|k\PP{}$;\C{ zastav se na prvnim ukazateli na pattern }\2\6 \&{switch} ${}(\|m\K\\{match}(\\{lapi}[\|k])){}$\5 ${}\{{}$\1\6 \4\&{case} \.{FOUND}:\5 ${}({*}\\{lapt}[\|k]\MG\\{proc})(\,){}$;\C{ Pattern nalezen, spustit proceduru }\6 \4\&{case} \.{NOFOUND}:\5 ${}\\{lapi}[\|k]\K\NULL{}$;\C{ Deaktivace patternu }\6 ${}\\{ap}\MM;{}$\6 \&{break};\6 \4\&{default}:\6 \&{while} ${}(\|m\MM){}$\1\5 ${}\\{lapi}[\|k]\K\\{lapi}[\|k]\MG\\{next}{}$;\C{ dalsi pozice patternu }\2\6 \4${}\}{}$\2\6 ${}\|k\PP;{}$\6 ${}\|n\MM;{}$\6 \4${}\}{}$\2\par \U26.\fi \M{30}Při otevírání nových patternů, které nejsou v tuto chvíli zablokovány, se hned vypořádáme s takovými patterny, které nám dávají rovnou odpověď typu \PB{\.{FOUND}} nebo \PB{\.{NOFOUND}}. V takových případech ani nezanášíme ukazatel na pozici do pole \PB{\\{lapi}}. \Y\B\4\X30:Otevři nové patterny\X${}\E{}$\6 $\\{pp}\K\\{listpatt};{}$\6 \&{while} ${}(\\{pp}\I\NULL){}$\5 ${}\{{}$\1\6 \&{switch} ${}(\|m\K\\{match}(\\{pp}\MG\\{patt})){}$\5 ${}\{{}$\1\6 \4\&{case} \.{FOUND}:\5 ${}({*}\\{pp}\MG\\{proc})(\,){}$;\C{ spustit proceduru }\6 \4\&{case} \.{NOFOUND}:\5 \&{break};\6 \4\&{default}:\5 \X31:Vytvoř ukazatel na nový pattern a \PB{\&{break}}\X;\6 \4${}\}{}$\2\6 ${}\\{pp}\K\\{pp}\MG\\{next};{}$\6 \4${}\}{}$\2\par \U26.\fi \M{31}Není-li hned známa odpověď, zda pattern vyhovuje či nikoli, překontrolujeme nejdříve, zda už není pattern ve stejné pozici otevřený. Pak najdeme první \uv{díru} v tabulce \PB{\\{lapi}} a tam uhnízdíme nový ukazatel na pozici v patternu. \Y\B\4\X31:Vytvoř ukazatel na nový pattern a \PB{\&{break}}\X${}\E{}$\6 $\\{pi}\K\\{pp}\MG\\{patt};{}$\6 \&{while} ${}(\|m\MM){}$\1\5 ${}\\{pi}\K\\{pi}\MG\\{next};{}$\2\6 ${}\|n\K\\{ap};{}$\6 ${}\|k\K\T{0};{}$\6 \&{while} (\|n)\5 ${}\{{}$\1\6 \&{if} ${}(\\{lapi}[\|k]\E\\{pi}){}$\1\5 \&{break};\2\6 \&{if} ${}(\\{lapi}[\|k\PP]\I\NULL){}$\1\5 ${}\|n\MM;{}$\2\6 \4${}\}{}$\2\6 \&{if} ${}(\R\|n){}$\5 ${}\{{}$\1\6 ${}\|k\K\T{0};{}$\6 \&{while} ${}(\\{lapi}[\|k]\I\NULL){}$\1\5 ${}\|k\PP;{}$\2\6 \&{if} ${}(\|k\G\.{MAXPATT}){}$\5 ${}\{{}$\1\6 ${}\\{fprintf}(\\{stderr},\39\.{"I\ cannot\ allocate\ p}\)\.{p,\ is\ anything\ wrong}\)\.{?\\n"});{}$\6 \\{exit}(\.{BAD\_PROGRAM});\6 \4${}\}{}$\2\6 ${}\\{lapt}[\|k]\K\\{pp};{}$\6 ${}\\{lapi}[\|k]\K\\{pi};{}$\6 ${}\\{ap}\PP;{}$\6 \4${}\}{}$\2\par \U30.\fi \M{32}Poslední věcí ve funci \PB{\\{tie}} je tisk závěrečné statistiky zpracování. \Y\B\4\X32:Tiskni závěrečnou zprávu\X${}\E{}$\6 $\\{fprintf}(\\{stderr},\39\.{"\~\~\~\ file:\ \%s\\t\ \ lin}\)\.{es:\ \%ld,\ changes:\ \%l}\)\.{d\\n"},\39\\{filename},\39\\{numline},\39\\{numchanges}){}$;% \par \U26.\fi \N{1}{33}Inicializace patternů. Po vytvoření předchozího kódu opírajícího se o~patterny máme nyní v~ruce poměrně silný nástroj na definování různých činností programu prostým vytvořením patternu a příslušné jeho procedury. Pokud budeme chtít v~budoucnu nějaký rys programu přidat, pravděpodobně to bude snadné. Nejprve deklarujeme některé často používané skupiny znaků v~patternech. \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{char} \\{tblanks}[\,]${}\K\.{"\ \~\\t"};{}$\6 \&{char} \\{blanks}[\,]${}\K\.{"\ \\t"};{}$\6 \&{char} \\{blankscr}[\,]${}\K\.{"\ \\t\\n"};{}$\6 \&{char} \\{tblankscr}[\,]${}\K\.{"\ \~\\t\\n"};{}$\6 \&{char} \\{nochar}[\,]${}\K\.{"\%\~\\n"};{}$\6 \&{char} \\{cr}[\,]${}\K\.{"\\n"};{}$\6 \&{char} \\{prefixes}[\,]${}\K\.{"[(\{\~"};{}$\6 \&{char} \\{dolar}[\,]${}\K\.{"\$"};{}$\6 \&{char} \\{backslash}[\,]${}\K\.{"\\\\"};{}$\6 \&{char} \\{openbrace}[\,]${}\K\.{"\{"};{}$\6 \&{char} \\{letters}[\,]${}\K\.{"abcdefghijklmnopqrs}\)\.{tuvwxyzABCDEFGHIJKLM}% \)\.{NOPQRSTUVWXYZ"};{}$\6 \&{PATTERN} ${}{*}\\{vlnkalist},{}$ ${}{*}\\{mathlist},{}$ ${}{*}% \\{parcheck},{}$ ${}{*}\\{verblist}{}$;\par \fi \M{34}Začneme definicí nejčastěji používaného patternu na vlnkování uvnitř řádku. Připomeňme, že opakované volání funkce \PB{\\{setpattern}} vytváří interně seznam patternů, přičemž o~jejich propojení se nemusíme starat. Vyzvedneme si z~návratového kódu funkce pouze ukazatel na první položku seznamu \PB{\\{normallist}}. Stejně tak opakované volání funkce \PB{\\{setpi}} vytváří seznam pozic pro naposledy deklarovaný pattern. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 $\\{vlnkalist}\K\\{setpattern}(\\{vlnkain});{}$\6 ${}\\{setpi}(\\{tblankscr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{tblanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{prefixes},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{charset},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{nochar},\39\.{ONE\_NOT}){}$;\par \fi \M{35}\B\X27:Inicializace proměnných při startu funkce \PB{\\{tie}}\X${}% \mathrel+\E{}$\6 $\\{listpatt}\K\\{normallist}\K\\{vlnkalist}{}$;\par \fi \M{36}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{vlnkain}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{char} \|p;\7 ${}\\{ind}\MM;{}$\6 ${}\|p\K\\{buff}[\\{ind}\MM];{}$\6 \&{while} ${}(\\{strchr}(\\{blanks},\39\\{buff}[\\{ind}])\I\NULL){}$\1\5 ${}\\{ind}\MM;{}$\2\6 ${}\\{ind}\PP;{}$\6 ${}\\{buff}[\\{ind}\PP]\K\.{'\~'};{}$\6 ${}\\{buff}[\\{ind}\PP]\K\|p;{}$\6 ${}\\{numchanges}\PP;{}$\6 \4${}\}{}$\2\par \fi \M{37}Podobně pro tvorbu vlnky \uv{přes řádek} vytvoříme pattern a kód procedury. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 \\{setpattern}(\\{vlnkacr});\6 ${}\\{setpi}(\\{tblankscr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{tblanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{prefixes},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{charset},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{cr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{nochar},\39\.{ONE\_NOT}){}$;\par \fi \M{38}V proceduře k tomuto patternu musíme ošetřit případ typu \uv{\.{a\char126v\char92np}}, kdy nelze prostě přehodit \uv{\.{\char92n}} za \uv{\.{v}}, protože bychom roztrhli mezeru svázanou vlnkou už dříve. Proto musíme vyhledat vhodné místo pro roztržení řádku, které bude až {\it před\/} znakem \uv{\.{a}}. Při důsledném ošetření tohoto fenoménu můžeme dokonce narazit na situaci \uv{\.{\char92n\ v\char126v\char126v\char92np}}, kde nemůžeme vložit \uv{\.{\char92n}} před první výskyt \uv{\.{v}}, protože bychom dostali \uv{\.{\char92n\char92n}}, tedy prázdný řádek. Ten je v \TeX{}u interperetován odlišně. V této výjimečné situaci pouze zrušíme stávající (v pořadí druhé) \uv{\.{\char92n}} a nebudeme vytvářet nové. Na výstupu bude soubor o jeden řádek kratší. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{vlnkacr}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{char} \|p;\6 \&{int} \|i${},{}$ \|j;\7 ${}\\{ind}\MM;{}$\6 ${}\|p\K\\{buff}[\\{ind}\MM];{}$\6 \&{while} ${}(\\{strchr}(\\{blankscr},\39\\{buff}[\\{ind}])\I\NULL){}$\1\5 ${}\\{ind}\MM;{}$\2\6 ${}\|i\K\\{ind}{}$;\C{ misto predlozky, kterou chceme vazat }\6 \&{while} ${}(\|i\G\T{0}\W(\\{strchr}(\\{blankscr},\39\\{buff}[\|i])\E% \NULL)){}$\1\5 ${}\|i\MM;{}$\2\6 ${}\|j\K\|i;{}$\6 \&{while} ${}(\|i\G\T{0}\W(\\{strchr}(\\{blanks},\39\\{buff}[\|i])\I\NULL)){}$% \1\5 ${}\|i\MM;{}$\2\6 \&{if} ${}(\|i\G\T{0}\W\\{buff}[\|i]\E\.{'\\n'}){}$\1\5 ${}\|j\K{-}\T{1};{}$\2\6 \&{if} ${}(\|j\G\T{0}){}$\1\5 ${}\\{buff}[\|j]\K\.{'\\n'};{}$\2\6 \&{else}\1\5 ${}\\{numline}\MM;{}$\2\6 ${}\\{ind}\PP;{}$\6 ${}\\{buff}[\\{ind}\PP]\K\.{'\~'};{}$\6 ${}\\{buff}[\\{ind}\PP]\K\|p;{}$\6 ${}\\{numchanges}\PP;{}$\6 \4${}\}{}$\2\par \fi \M{39}Nyní vytvoříme patterny pro případy typu \.{\char92uv\char`\{v lese\char`% \}}. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 \\{setpattern}(\\{vlnkain});\C{ na radku }\6 ${}\\{setpi}(\\{tblankscr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{backslash},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{letters},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{letters},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{openbrace},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{prefixes},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{charset},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{nochar},\39\.{ONE\_NOT});{}$\6 \\{setpattern}(\\{vlnkacr});\C{ pres radek }\6 ${}\\{setpi}(\\{tblankscr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{backslash},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{letters},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{letters},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{openbrace},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{prefixes},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{charset},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{cr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{nochar},\39\.{ONE\_NOT}){}$;\par \fi \M{40}Vytvoříme patterny a proceduru pro potlatčení tvorby vlnky u písmen těsně následujících sekvence \.{\char92TeX} a \.{\char92LaTeX}. Tj. nechceme, aby např z textu \uv{\.{Vlastnosti~\char92TeX~u~jsou...}} jsme dostali text s nesprávně vázaným písmenem \uv{\.{Vlastnosti~\char92TeX~u\char126jsou...}}. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 $\\{normalpattern}(\\{tielock},\39\.{"\\\\TeX"});{}$\6 ${}\\{setpi}(\\{blankscr},\39\.{ONE});{}$\6 ${}\\{normalpattern}(\\{tielock},\39\.{"\\\\LaTeX"});{}$\6 ${}\\{setpi}(\\{blankscr},\39\.{ONE}){}$;\par \fi \M{41}Procedura \PB{\\{tielock}} obsahuje nečistý trik. Při provádění procedury je právě načten znak z \PB{\\{blankscr}} a je uložen do buff. Testy na otevírání nových patternů pro tento znak teprve budou následovat a testují se na hodnotu proměnné \PB{\|c}. Stačí tedy změnit hodnotu \PB{\|c} a vlnkovací patterny se neotevřou. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{tielock}(\,)\1\1\2\2\6 ${}\{{}$\1\6 ${}\|c\K\T{1};{}$\6 \4${}\}{}$\2\par \fi \M{42}Ošetříme nyní přechod do/z matematického režimu \TeX{}u. Uvnitř math módu vlnky neděláme. Při zjištěném nesouladu v přechodech mezi math-módy spustíme následující proceduru. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{printwarning}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\R\\{silent}){}$\1\5 ${}\\{fprintf}(\\{stderr},\39\.{"\~!\~\ warning:\ text/m}\)\.{ath/verb\ mode\ mismat}\)\.{ch,\ \ file:\ \%s,\ \ line}\)\.{:\ \%ld\\n"},\39\\{filename},\39% \\{numline}-(\|c\E\.{'\\n'}\?\T{1}:\T{0}));{}$\2\6 ${}\\{status}\K\.{WARNING};{}$\6 \4${}\}{}$\2\par \fi \M{43}Začneme patterny pro přechod do/z matematického režimu, ohraničeného jedním dolarem, nebo v La\TeX{}u příslušnými sekvencemi. Sekvence La\TeX{}u \.{\char92(} a \.{\char92)} nejsou zahrnuty, protože bývají často předefinovány k jiným užitečnějším věcem. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 \&{if} ${}(\R\\{nomath}){}$\5 ${}\{{}$\1\6 ${}\\{mathlist}\K\\{setpattern}(\\{onedollar});{}$\6 ${}\\{setpi}(\\{dolar},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{dolar},\39\.{ONE\_NOT});{}$\6 \&{if} (\\{latex})\5 ${}\{{}$\1\6 ${}\\{normalpattern}(\\{mathin},\39\.{"\\\\begin.\{math\}"});{}$\6 ${}\\{normalpattern}(\\{mathout},\39\.{"\\\\end.\{math\}"});{}$\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \fi \M{44}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{mathin}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{TEXTMODE}){}$\1\5 \\{printwarning}(\,);\2\6 ${}\\{mode}\K\.{MATHMODE};{}$\6 ${}\\{normallist}\K\\{listpatt}\K\\{mathlist};{}$\6 \4${}\}{}$\2\7 \&{void} \\{mathout}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{MATHMODE}){}$\1\5 \\{printwarning}(\,);\2\6 ${}\\{mode}\K\.{TEXTMODE};{}$\6 ${}\\{normallist}\K\\{listpatt}\K\\{vlnkalist};{}$\6 \4${}\}{}$\2\par \fi \M{45}Při programování procedury \PB{\\{onedollar}} nesmíme zapomenout na výskyt sekvence \.{\char92\$}. V tom případě akci ignorujeme. Podobně u sekvence \.{\$\$} souhlasí ten druhý dolar s naším patternem, ale to už jsme uvnitř display módu. V takovém případě také nic neděláme. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{onedollar}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{buff}[\\{ind}-\T{3}]\E\.{'\\\\'}\V(\\{buff}[\\{ind}-\T{3}]\E\.{'% \$'}\W\\{buff}[\\{ind}-\T{4}]\I\.{'\\\\'})){}$\1\5 \&{return};\2\6 \&{if} ${}(\\{mode}\E\.{DISPLAYMODE}){}$\1\5 \\{printwarning}(\,);\2\6 \&{else}\5 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\E\.{TEXTMODE}){}$\1\5 \\{mathin}(\,);\2\6 \&{else}\1\5 \\{mathout}(\,);\2\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \fi \M{46}Pokud najdeme prázdný řádek, překontrolujeme, zda náhodou nejsme v math-módu. Pokud ano, vypíšeme varování a přejdeme do textového módu. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 $\\{parcheck}\K\\{setpattern}(\\{checkmode});{}$\6 ${}\\{setpi}(\\{cr},\39\.{ONE});{}$\6 ${}\\{setpi}(\\{blanks},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{cr},\39\.{ONE}){}$;\par \fi \M{47}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{checkmode}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{TEXTMODE}){}$\5 ${}\{{}$\1\6 \\{printwarning}(\,);\6 ${}\\{mode}\K\.{TEXTMODE};{}$\6 ${}\\{normallist}\K\\{listpatt}\K\\{vlnkalist};{}$\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \fi \M{48}Nyní ošetříme výskyt dvou dolarů, tj. vstup do/z display módu. Rovněž mysleme na La\TeX{}isty a jejich prostředí pro display-mód. Protože je možná alternativa s hvězdičkou na konci názvu prostředí, raději už uzavírací závorku do patternu nezahrnujeme. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 \&{if} ${}(\R\\{nomath}){}$\5 ${}\{{}$\1\6 ${}\\{normalpattern}(\\{twodollars},\39\.{"\$\$"});{}$\6 \&{if} (\\{latex})\5 ${}\{{}$\1\6 ${}\\{normalpattern}(\\{displayin},\39\.{"\\\\begin.\{displaymat}\)\.{h"});{}$\6 ${}\\{normalpattern}(\\{displayin},\39\.{"\\\\begin.\{equation"});{}$\6 ${}\\{normalpattern}(\\{displayout},\39\.{"\\\\end.\{displaymath"});{}$\6 ${}\\{normalpattern}(\\{displayout},\39\.{"\\\\end.\{equation"});{}$\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \fi \M{49}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{displayin}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{TEXTMODE}){}$\1\5 \\{printwarning}(\,);\2\6 ${}\\{mode}\K\.{DISPLAYMODE};{}$\6 ${}\\{normallist}\K\\{listpatt}\K\\{parcheck};{}$\6 \4${}\}{}$\2\7 \&{void} \\{displayout}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{DISPLAYMODE}){}$\1\5 \\{printwarning}(\,);\2\6 ${}\\{mode}\K\.{TEXTMODE};{}$\6 ${}\\{normallist}\K\\{listpatt}\K\\{vlnkalist};{}$\6 \4${}\}{}$\2\7 \&{void} \\{twodollars}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{buff}[\\{ind}-\T{3}]\E\.{'\\\\'}){}$\1\5 \&{return};\2\6 \&{if} ${}(\\{mode}\E\.{DISPLAYMODE}){}$\1\5 \\{displayout}(\,);\2\6 \&{else}\1\5 \\{displayin}(\,);\2\6 \4${}\}{}$\2\par \fi \M{50}Následuje ošetření tzv. verbatim módu. Pro plain i La\TeX{} jsou nejčastější závorky pro verbatim mod tyto (variantu s \.{\char92begtt} používám s oblibou já). \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 \&{if} ${}(\R\\{noverb}){}$\5 ${}\{{}$\1\6 ${}\\{verblist}\K\\{normalpattern}(\\{verbinchar},\39\.{"\\\\verb"});{}$\6 ${}\\{setpi}(\\{blankscr},\39\.{ANY});{}$\6 ${}\\{setpi}(\\{blankscr},\39\.{ONE\_NOT});{}$\6 ${}\\{normalpattern}(\\{verbin},\39\.{"\\\\begtt"});{}$\6 \&{if} (\\{latex})\1\5 ${}\\{normalpattern}(\\{verbin},\39\.{"\\\\begin.\{verbatim"});{}$\2\6 \4${}\}{}$\2\6 \&{if} (\\{web})\5 ${}\{{}$\1\6 ${}\\{normalpattern}(\\{verbin},\39\.{"@<"});{}$\6 ${}\\{normalpattern}(\\{verbin},\39\.{"@d"});{}$\6 \4${}\}{}$\2\6 \&{if} ${}(\R\\{noverb}){}$\5 ${}\{{}$\1\6 ${}\\{verboutlist}[\T{0}]\K\\{setpattern}(\\{verbout});{}$\6 ${}\\{setpi}(\\{verbchar},\39\.{ONE});{}$\6 ${}\\{verboutlist}[\T{1}]\K\\{normalpattern}(\\{verbout},\39\.{"% \\\\endtt"});{}$\6 \&{if} (\\{latex})\1\5 ${}\\{verboutlist}[\T{2}]\K\\{normalpattern}(\\{verbout},\39\.{"\\\\end% \{verbatim"});{}$\2\6 \4${}\}{}$\2\6 \&{if} (\\{web})\5 ${}\{{}$\1\6 ${}\\{verboutlist}[\T{3}]\K\\{normalpattern}(\\{verbout},\39\.{"@\ "});{}$\6 ${}\\{normalpattern}(\\{verbout},\39\.{"@*"});{}$\6 ${}\\{normalpattern}(\\{verbout},\39\.{"@>|"});{}$\6 \4${}\}{}$\2\par \fi \M{51}Procedura \PB{\\{verbinchar}} se od \uv{společné} procedury \PB{% \\{verbin}} liší v tom, že zavede do stringu \PB{\\{verbchar}} momentální hodnotu proměnné \PB{% \|c}. Proto druhý výskyt této hodnoty verbatim režim ukončí. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{int} \\{prevmode};\6 \&{PATTERN} ${}{*}\\{prevlist},{}$ ${}{*}\\{verboutlist}[\T{4}];{}$\6 \&{char} \\{verbchar}[\T{2}];\7 \&{void} \\{verbinchar}(\,)\1\1\2\2\6 ${}\{{}$\1\6 ${}\\{prevmode}\K\\{mode};{}$\6 ${}\\{verbchar}[\T{0}]\K\|c;{}$\6 ${}\|c\K\T{1};{}$\6 ${}\\{listpatt}\K\\{normallist}\K\\{verboutlist}[\T{0}];{}$\6 ${}\\{prevlist}\K\\{listpatt}\MG\\{next};{}$\6 ${}\\{listpatt}\MG\\{next}\K\NULL;{}$\6 ${}\\{mode}\K\.{VERBMODE};{}$\6 \4${}\}{}$\2\par \fi \M{52}Při programování \uv{obecné} funkce \PB{\\{verbin}} musíme dbát na to, aby zůstal aktivní pouze odpovídající \uv{výstupní} pattern k danému vstupnímu. Také si zapamatujeme mód, ze kterého jsme do verbatim oblasti vstoupili, abychom se k němu mohli vrátit (např. uvnitř math. módu může být \.{\char92hbox} a v něm lokálně verbatim konstrukce). \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{verbin}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{int} \|i;\7 ${}\|i\K\T{0};{}$\6 ${}\\{prevmode}\K\\{mode};{}$\6 \&{switch} (\|c)\5 ${}\{{}$\1\6 \4\&{case} \.{'t'}:\5 ${}\|i\K\T{1};{}$\6 \&{break};\6 \4\&{case} \.{'m'}:\5 ${}\|i\K\T{2};{}$\6 \&{break};\6 \4\&{case} \.{'<'}:\5 ;\6 \4\&{case} \.{'d'}:\5 ${}\|i\K\T{3};{}$\6 \&{if} ${}(\\{buff}[\\{ind}-\T{3}]\E\.{'@'}){}$\1\5 \&{return};\C{ dvojity @ ignorovat }\2\6 \&{break};\6 \4${}\}{}$\2\6 ${}\\{listpatt}\K\\{normallist}\K\\{verboutlist}[\|i];{}$\6 ${}\\{prevlist}\K\\{listpatt}\MG\\{next};{}$\6 \&{if} ${}(\|c\I\.{'<'}\W\|c\I\.{'d'}){}$\1\5 ${}\\{listpatt}\MG\\{next}\K\NULL;{}$\2\6 ${}\\{mode}\K\.{VERBMODE};{}$\6 \4${}\}{}$\2\par \fi \M{53}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{verbout}(\,)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{VERBMODE}){}$\1\5 \&{return};\2\6 \&{if} ${}(\\{web}\W\\{buff}[\\{ind}-\T{2}]\E\.{'@'}\W\\{buff}[\\{ind}-\T{3}]\E% \.{'@'}){}$\1\5 \&{return};\2\6 ${}\\{mode}\K\\{prevmode};{}$\6 ${}\\{normallist}\MG\\{next}\K\\{prevlist};{}$\6 \&{switch} (\\{mode})\5 ${}\{{}$\1\6 \4\&{case} \.{DISPLAYMODE}:\5 ${}\\{normallist}\K\\{listpatt}\K\\{parcheck};{}$\6 \&{break};\6 \4\&{case} \.{MATHMODE}:\5 ${}\\{normallist}\K\\{listpatt}\K\\{mathlist};{}$\6 \&{break};\6 \4\&{case} \.{TEXTMODE}:\5 ${}\\{normallist}\K\\{listpatt}\K\\{vlnkalist};{}$\6 \&{break};\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \fi \M{54}Nyní implementujeme vlastnost dříve používaného programu vlnka, tj. že lze jeho činnost vypnout a opět zapnout v komentářích. Vytváříme druhý nezávislý seznam patternů a proto nejprve pronulujeme \PB{\\{lastpt}}. \Y\B\4\X21:Inicializace datových struktur\X${}\mathrel+\E{}$\6 $\\{lastpt}\K\T{0};{}$\6 ${}\\{commentlist}\K\\{normalpattern}(\\{tieoff},\39\.{"\%.\~.-"});{}$\6 ${}\\{normalpattern}(\\{tieon},\39\.{"\%.\~.+"}){}$;\par \fi \M{55}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{void} \\{tieoff}(\,)\1\1\2\2\6 ${}\{{}$\1\6 ${}\\{normallist}\K\NULL;{}$\6 \4${}\}{}$\2\7 \&{void} \\{tieon}(\,)\1\1\2\2\6 ${}\{{}$\1\6 ${}\\{normallist}\K\\{vlnkalist};{}$\6 \4${}\}{}$\2\par \fi \M{56}Další plánovaná vylepšení. Program by mohl číst definici svého chování nejen z~příkazové řádky, ale v~mnohem kompletnější podobě, včetně uživatelsky definovaných patternů, z komentářové oblasti ve čteném souboru. Parametry zde uvedené by mohly mít vyšší prioritu, než parametry z~příkazové řádky a mohl by se třeba rozšiřovat seznam sekvencí, za nimiž písmena nemají být vázana vlnkou (zatím je implemenováno na pevno jen \.{\char92TeX} a \.{\char92LaTeX}). \fi \N{1}{57}Rejstřík. \fi \inx \fin \con