\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} \.{<stdio.h>}\6
\8\#\&{include} \.{<string.h>}\6
\8\#\&{include} \.{<stdlib.h>}\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
