\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 \X29: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.5,\ (c)\ }\)\.{1995,\ 2002,\ 2009,\ 20}\)\.{10\ Petr\ Olsak\\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, 8, 17, 18, 19, 23, 28\ETs36. \U1.\fi \M{5}Základní rozvržení funkce \PB{\\{main}}. \Y\B\4\X5:Hlavní program\X${}\E{}$\6 \&{int} \\{main}(\&{int} \\{argc}${},\39{}$\&{char} ${}{*}{*}\\{argv}){}$\1\1\2% \2\6 ${}\{{}$\1\6 \X11:Lokální proměnné funkce \PB{\\{main}}\X;\6 ${}\\{prog\_name}\K\\{argv}[\T{0}];{}$\6 ${}\\{status}\K\.{OK};{}$\6 \X9:Načtení parametrů příkazového řádku\X;\6 \&{if} ${}(\R\\{silent}){}$\1\5 ${}\\{fprintf}(\\{stderr},\39\.{BANNER});{}$\2\6 \X24:Inicializace datových struktur\X;\6 \X13: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{$\X13: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ě. * \.{-x} \dots\ parametr vymezuje pomocí hexadecimálního zápisu string, který program vkládá na vyhledaná místa. Implicitně vkládá vlnku. Například \.{-x C2A0} způsobí, že program bude vkládat místo vlnky dva byty, první s kódem \.{C2} a druhý s kódem \.{A0}. * \.{-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 \&{static} \&{void} \\{printusage}(\&{void})\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"}\.{"\ \ \ \ \ \ -x\ code\ :\ cod}\)\.{e\ for\ tie\ symbol,\ de}\)\.{fault:\ 7E,\ example\ -}\)\.{x\ C2A0\\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, 12, 20, 21, 22, 25, 26, 39, 41, 44, 45, 47, 48, 50, 52, 54, 55, 56\ETs58. \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}String \PB{\\{tiestr}} obsahuje string, kterým se má nahradit vyhledané místo. Pokud není použit parametr \.{-u}, je tento string jadnoznakový a obsahuje vlnku. Jinak obsahuje string konvertovaný z parametru \.{-u}. String má délku \PB{\\{tiestrlen}} bez ohledu na to, zda obsahuje nebo neobsahuje nulové znaky (C-čková konvence pro stringy není použita). \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{unsigned} \&{char} \\{tiestr}[\.{MAXLEN}];\6 \&{int} \\{tiestrlen};\par \fi \M{9}\B\X9:Načtení parametrů příkazového řádku\X${}\E{}$\6 $\\{tiestr}[\T{0}]\K\.{'\~'};{}$\6 ${}\\{tiestrlen}\K\T{1};{}$\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} \.{'x'}:\6 \&{if} ${}(\\{argc}<\T{2}){}$\1\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\2\6 ${}\\{argv}\PP;{}$\6 ${}\\{argc}\MM;{}$\6 \\{settiestr}(\\{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 \M{10}Vyřešíme konverzi kódu zapsaného za parametrem \.{-x} na string \PB{% \\{tiestr}}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{unsigned} \&{char} \\{hexnum}(\&{char} \|c)\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\|c\G\.{'0'}\W\|c\Z\.{'9'}){}$\1\5 \&{return} \|c${}-\.{'0'};{}$\2\6 \&{if} ${}(\|c\G\.{'A'}\W\|c\Z\.{'F'}){}$\1\5 \&{return} \|c${}-\.{'A'}+\T{10};{}$\2\6 \&{if} ${}(\|c\G\.{'a'}\W\|c\Z\.{'f'}){}$\1\5 \&{return} \|c${}-\.{'a'}+\T{10};{}$\2\6 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\6 \4${}\}{}$\2\7 \&{static} \&{void} \\{settiestr}(\&{char} ${}{*}\|s){}$\1\1\2\2\6 ${}\{{}$\1\6 \&{int} \|i${},{}$ \|j;\7 ${}\|i\K\\{strlen}(\|s);{}$\6 \&{if} ${}((\|i>\T{2}*\.{MAXLEN})\V\|i\MOD\T{2}\E\T{1}){}$\1\5 ${}\\{printusage}(\,),\39\\{exit}(\.{BAD\_OPTIONS});{}$\2\6 ${}\\{tiestrlen}\K\|i/\T{2};{}$\6 ${}\|j\K\T{0};{}$\6 \&{for} ${}(\|i\K\T{0};{}$ ${}\|i<\\{tiestrlen};{}$ ${}\|i\PP){}$\5 ${}\{{}$\1\6 ${}\\{tiestr}[\|i]\K\\{hexnum}(\|s[\|j\PP])\LL\T{4};{}$\6 ${}\\{tiestr}[\|i]\MRL{+{\K}}\\{hexnum}(\|s[\|j\PP]);{}$\6 \4${}\}{}$\2\6 \4${}\}{}$\2\par \fi \N{1}{11}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\X11:Lokální proměnné funkce \PB{\\{main}}\X${}\E{}$\6 \&{FILE} ${}{*}\\{infile},{}$ ${}{*}\\{outfile};{}$\6 \&{char} \\{backup}[\.{MAXLEN}];\6 \&{int} \|j;\par \U5.\fi \M{12}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 \&{static} \&{void} \\{ioerr}(\&{char} ${}{*}\|f){}$\1\1\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{13}Způsob zpracování souborů rozlišíme podle režimu daným přepínačem \.{-f}. \Y\B\4\X13:Zpracování souborů\X${}\E{}$\6 \&{if} (\\{isfilter}) \X14:Zpracování v režimu filter\X\6 \&{else} \X15:Zpracování všech souborů příkazové řádky\X\par \Q6. \U5.\fi \M{14}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\X14: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\.{"wb"});{}$\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 \U13.\fi \M{15}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\X15: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 \X16: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\.{"wb"});{}$\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 \U13.\fi \M{16}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\X16: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 \U15.\fi \N{1}{17}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})(\&{void}){}$;\C{ procedura spustena pri souhlasu patternu }\7 \&{struct} \&{PATTERN} ${}{*}\\{next}{}$;\C{ nasledujici v seznamu vsech patternu }\2\6 ${}\}{}$ \&{PATTERN};\par \fi \M{18}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} ${}{*}\\{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{19}Dne 30. 4. 2009 jsem přidal možnost čtení vstupu, který obsahuje nulové byty. Takové nuly se přepisují do výstupu, ale program si jich nevšímá při procházení patternů. Tím je možno program použít na soubory kódované v UTF16, ačkoli patterny obsahují jen jednobytové ASCII znaky. Buffer \PB{\\{buff}} může obsahovat i nulové byty, které je třeba přepsat do výstupu. Na druhé straně buffer \PB{\\{buffnz}} obsahuje jen nenulové byty, na které se někdy ptáme při pohledu dozadu. Nejdelší pohled dozadu je o čtyři byty. Udělám tedy \PB{\\{buffnz}} osmibytový, začnu jej plnit od \PB{\\{buffnz}}[4] a kdykoli je buffer zcela zaplněn, přesunu horní čtyři byty na spodní a dále pokračuji v plnění bufferu od pozice \PB{\\{buffnz}}[4]. \Y\B\4\X4:Globální deklarace\X${}\mathrel+\E{}$\6 \&{char} \\{buffnz}[\T{8}];\6 \&{int} \\{inz};\par \fi \M{20}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 \&{static} \&{void} ${}{*}{}$\\{myalloc}(\&{int} \\{size})\1\1\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{21}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 \&{static} \&{PATTERN} ${}{*}\\{setpattern}(\&{void}({*}\\{proc})(\&{void})){}$% \1\1\2\2\6 ${}\{{}$\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{22}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 \&{static} \&{void} \\{setpi}(\&{char} ${}{*}\\{str},\39{}$\&{int} \\{flag})\1% \1\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{23}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{24}Inicializujeme toto pole (znak, nula, znak, nula, atd...). \Y\B\4\X24: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 \As37, 40, 42, 43, 46, 49, 51, 53\ETs57. \U5.\fi \M{25}Definujme funkci \PB{\\{normalpattern}}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{PATTERN} ${}{*}\\{normalpattern}(\&{void}({*}\\{proc})(\&{void}),% \39{}$\&{const} \&{char} ${}{*}\\{str}){}$\1\1\2\2\6 ${}\{{}$\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{26}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 \&{static} \&{int} \\{match}(\&{PATITEM} ${}{*}\|p){}$\1\1\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 \X27: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{27}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\X27: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 \U26.\fi \N{1}{28}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{29}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\X29:Vlnkovací funkce \PB{\\{tie}}\X${}\E{}$\6 \&{static} \&{void} \\{tie}(\&{FILE} ${}{*}\\{input},\39{}$\&{FILE} ${}{*}% \\{output}){}$\1\1\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 \X30:Inicializace proměnných při startu funkce \PB{\\{tie}}\X;\6 \&{while} ${}(\R\\{feof}(\\{input})){}$\5 ${}\{{}$\1\6 \&{if} ${}(\\{ap}\E\T{0}\W\\{ind}>\.{BUFI}\W\|c\I\.{'\\\\'}){}$\1\5 \X31:Vyprázdni buffer\X;\2\6 \X33:Otevři nové patterny\X;\C{ 1. 2. 2010: prohozene poradi }\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\T{0}){}$\1\5 \&{continue};\C{ 30. 4. 2009 }\2\6 \&{if} ${}(\\{inz}\G\T{8}){}$\5 ${}\{{}$\1\6 \&{for} ${}(\\{inz}\K\T{0};{}$ ${}\\{inz}<\T{4};{}$ ${}\\{inz}\PP){}$\1\5 ${}\\{buffnz}[\\{inz}]\K\\{buffnz}[\\{inz}+\T{4}];{}$\2\6 ${}\\{inz}\K\T{4};{}$\6 \4${}\}{}$\2\6 ${}\\{buffnz}[\\{inz}\PP]\K\|c;{}$\6 \&{if} ${}(\|c\E\.{'\\n'}){}$\1\5 ${}\\{numline}\PP,\39\\{listpatt}\K\\{normallist};{}$\2\6 \&{if} ${}(\|c\E\.{'\%'}\W\\{mode}\I\.{VERBMODE}\W\\{buffnz}[\\{inz}-\T{2}]\I% \.{'\\\\'}){}$\1\5 ${}\\{listpatt}\K\\{commentlist};{}$\2\6 \X32:Projdi otevřené patterny\X;\6 \4${}\}{}$\2\6 \X31:Vyprázdni buffer\X;\6 \&{if} ${}(\R\\{web}){}$\1\5 \\{checkmode}(\,);\C{ zaverecna kontrola modu }\2\6 \&{if} ${}(\R\\{silent}){}$\1\5 \X35:Tiskni závěrečnou zprávu\X;\2\6 \4${}\}{}$\2\par \U1.\fi \M{30}\B\X30: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\T{1};{}$\6 ${}\\{mode}\K\\{ap}\K\T{0};{}$\6 ${}\\{ind}\K\T{1};{}$\6 \&{for} ${}(\\{inz}\K\T{0};{}$ ${}\\{inz}<\T{4};{}$ ${}\\{inz}\PP){}$\1\5 ${}\\{buffnz}[\\{inz}]\K\T{0};{}$\2\6 ${}\\{inz}\K\T{4};{}$\6 ${}\\{numline}\K\T{1};{}$\6 ${}\\{numchanges}\K\T{0};{}$\6 ${}\\{mode}\K\.{TEXTMODE}{}$;\par \A38. \U29.\fi \M{31}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. Od 30. 4. 2009 tento problém pominul, protože se ptáme dozadu pouze v~\PB{\\{buffnz}}, ale vlastnost dříve implementovanou v \PB{\\{buff}} jsem ponechal beze změny. \Y\B\4\X31:Vyprázdni buffer\X${}\E{}$\6 ${}\{{}$\1\6 ${}\\{fwrite}({\AND}\\{buff}[\T{1}],\39\\{ind}-\T{1},\39\T{1},\39% \\{output});{}$\6 ${}\\{ind}\K\T{1};{}$\6 \4${}\}{}$\2\par \U29.\fi \M{32}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\X32: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 \U29.\fi \M{33}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\X33:Otevři nové patterny\X${}\E{}$\6 $\\{pp}\K\\{listpatt};{}$\6 \&{if} (\|c)\1\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 \X34:Vytvoř ukazatel na nový pattern a \PB{\&{break}}\X;\6 \4${}\}{}$\2\6 ${}\\{pp}\K\\{pp}\MG\\{next};{}$\6 \4${}\}{}$\2\2\par \U29.\fi \M{34}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\X34: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 \U33.\fi \M{35}Poslední věcí ve funci \PB{\\{tie}} je tisk závěrečné statistiky zpracování. \Y\B\4\X35: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 \U29.\fi \N{1}{36}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{37}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\X24: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{38}\B\X30:Inicializace proměnných při startu funkce \PB{\\{tie}}\X${}% \mathrel+\E{}$\6 $\\{listpatt}\K\\{normallist}\K\\{vlnkalist}{}$;\par \fi \M{39}Vložení vlnky znamená vykonat následující práci: Zapamatovat si znak za skupinou mezer (do proměnné \PB{\|p}). Pokud před tímto znakem předchází nulový byte, použijeme ho později, proto si jej uložíme do proměnné \PB{\|z}. Dále se posuneme v bufferu vlevo přes všechny mezery, tabelátory (přesněji \PB{\\{blanks}}) a přeskakujeme přitom všechny nulové byty. Index % \PB{\\{ind}} se zastaví na předložce. Posuneme jej doprava za předložku (++\PB{\\{ind}}) a pokud tam je nulový byte a první znak \PB{\\{tiestr}} není nulový, posuneme se až za tento nulový byte. Dále vložíme string \PB{\\{tiestr}}, neboli vlnku. Nakonec připojíme zapamatovaný znak \PB{\|p}, ovšem pokud před ním byla nula, vložíme ji ještě před znak \PB{\|p}. \Y\B\4\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{void} \\{vlnkain}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 \&{int} \|i;\6 \&{char} \|p${},{}$ \|z;\7 ${}\\{ind}\MM;{}$\6 ${}\|p\K\\{buff}[\\{ind}\MM];{}$\6 ${}\|z\K\\{buff}[\\{ind}];{}$\6 \&{while} ${}(\R\\{buff}[\\{ind}]\V(\\{strchr}(\\{blanks},\39\\{buff}[\\{ind}])% \I\NULL)){}$\1\5 ${}\\{ind}\MM;{}$\2\6 \&{if} ${}(\R\\{buff}[\PP\\{ind}]\W\\{tiestr}[\T{0}]){}$\1\5 ${}\\{ind}\PP;{}$\2\6 \&{for} ${}(\|i\K\T{0};{}$ ${}\|i<\\{tiestrlen};{}$ ${}\|i\PP){}$\1\5 ${}\\{buff}[\\{ind}\PP]\K\\{tiestr}[\|i];{}$\2\6 ${}\|i\K\\{tiestrlen}{}$;\C{ nulu pred p vlozime, pokud je z==0 a }\6 \&{if} ${}(\R\\{tiestr}[\T{0}]){}$\1\5 ${}\|i\MM{}$;\C{ pocet vlozenych znaku z tiestr }\2\6 \&{if} ${}(\R\|z\W(\|i\MOD\T{2})){}$\1\5 ${}\\{buff}[\\{ind}\PP]\K\T{0}{}$;\C{ je sudy }\2\6 ${}\\{buff}[\\{ind}\PP]\K\|p;{}$\6 ${}\\{numchanges}\PP;{}$\6 \4${}\}{}$\2\par \fi \M{40}Podobně pro tvorbu vlnky \uv{přes řádek} vytvoříme pattern a kód procedury. \Y\B\4\X24: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{41}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 \&{static} \&{void} \\{vlnkacr}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 \&{char} \|p${},{}$ \|z;\6 \&{int} \|i${},{}$ \|j;\7 ${}\\{ind}\MM;{}$\6 ${}\|p\K\\{buff}[\\{ind}\MM];{}$\6 ${}\|z\K\\{buff}[\\{ind}];{}$\6 \&{while} ${}(\R\\{buff}[\\{ind}]\V(\\{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(\R\\{buff}[\\{ind}]\V(\\{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 \&{if} ${}(\R\\{buff}[\PP\\{ind}]\W\\{tiestr}[\T{0}]){}$\1\5 ${}\\{ind}\PP;{}$\2\6 \&{for} ${}(\|i\K\T{0};{}$ ${}\|i<\\{tiestrlen};{}$ ${}\|i\PP){}$\1\5 ${}\\{buff}[\\{ind}\PP]\K\\{tiestr}[\|i];{}$\2\6 ${}\|i\K\\{tiestrlen};{}$\6 \&{if} ${}(\R\\{tiestr}[\T{0}]){}$\1\5 ${}\|i\MM;{}$\2\6 \&{if} ${}(\R\|z\W(\|i\MOD\T{2})){}$\1\5 ${}\\{buff}[\\{ind}\PP]\K\T{0};{}$\2\6 ${}\\{buff}[\\{ind}\PP]\K\|p;{}$\6 ${}\\{numchanges}\PP;{}$\6 \4${}\}{}$\2\par \fi \M{42}Nyní vytvoříme patterny pro případy typu \.{\char92uv\char`\{v lese\char`% \}}. \Y\B\4\X24: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{43}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\X24: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{44}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 \PB{\\{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 \&{static} \&{void} \\{tielock}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 ${}\|c\K\T{1};{}$\6 \4${}\}{}$\2\par \fi \M{45}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 \&{static} \&{void} \\{printwarning}(\&{void})\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{46}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\X24: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{47}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{void} \\{mathin}(\&{void})\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 \&{static} \&{void} \\{mathout}(\&{void})\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{48}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 \&{static} \&{void} \\{onedollar}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{buffnz}[\\{inz}-\T{3}]\E\.{'\\\\'}\V(\\{buffnz}[\\{inz}-\T{3}]\E% \.{'\$'}\W\\{buffnz}[\\{inz}-\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{49}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\X24: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{50}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{void} \\{checkmode}(\&{void})\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{51}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\X24: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{52}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{void} \\{displayin}(\&{void})\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 \&{static} \&{void} \\{displayout}(\&{void})\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 \&{static} \&{void} \\{twodollars}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{buffnz}[\\{inz}-\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{53}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\X24: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{54}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 \&{static} \&{void} \\{verbinchar}(\&{void})\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{55}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 \&{static} \&{void} \\{verbin}(\&{void})\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} ${}(\\{buffnz}[\\{inz}-\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{56}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{void} \\{verbout}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 \&{if} ${}(\\{mode}\I\.{VERBMODE}){}$\1\5 \&{return};\2\6 \&{if} ${}(\\{web}\W\\{buffnz}[\\{inz}-\T{2}]\E\.{'@'}\W\\{buffnz}[\\{inz}-% \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{57}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\X24: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{58}\B\X6:Pomocné funkce\X${}\mathrel+\E{}$\6 \&{static} \&{void} \\{tieoff}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 ${}\\{normallist}\K\NULL;{}$\6 \4${}\}{}$\2\7 \&{static} \&{void} \\{tieon}(\&{void})\1\1\2\2\6 ${}\{{}$\1\6 ${}\\{normallist}\K\\{vlnkalist};{}$\6 \4${}\}{}$\2\par \fi \M{59}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}{60}Rejstřík. \fi \inx \fin \con