summaryrefslogtreecommitdiff
path: root/Build/source/utils/vlna/vlna-src/vlna.w
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/vlna/vlna-src/vlna.w')
-rw-r--r--Build/source/utils/vlna/vlna-src/vlna.w1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/Build/source/utils/vlna/vlna-src/vlna.w b/Build/source/utils/vlna/vlna-src/vlna.w
new file mode 100644
index 00000000000..8062a128b50
--- /dev/null
+++ b/Build/source/utils/vlna/vlna-src/vlna.w
@@ -0,0 +1,1070 @@
+
+\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}}
+
+
+@* 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ů:
+@c
+@<Hlavičkové soubory k načtení@>@/
+@<Globální deklarace@>@/
+@<Pomocné funkce@>@/
+@<Vlnkovací funkce |tie|@>@/
+@<Hlavní program@>
+
+@ Definujeme |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ý) |BANNER|.
+@d BANNER "This is program vlna, version 1.5, (c) 1995, 2002, 2009, 2010 Petr Olsak\n"
+
+@ V programu jsou použity knihovní funkce, jejíchž prototypy jsou
+definovány ve třech standardních hlavičkových souborech.
+@<Hlavičkové ...@>=
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+@ Definujeme konstanty pro návratový kód. |OK| pro úspěšný běh,
+|WARNING| při výskytu aspoň jedné varovné zprávy, |IO_ERR| pro chybu
+v~přístupu ke vtupním nebo výstupním souborům, |BAD_OPTIONS| pro
+syntaktickou chybu na příkazové řádce a |BAD_PROGRAM| pro případ
+havárie programu. Ta by neměla nikdy nastat. Proměnná |status| bude
+obsahovat návratový kód a proměnná |prog_name| bude ukazovat na text
+nultého parametru příkazové řádky.
+@d OK 0
+@d WARNING 1
+@d IO_ERR 2
+@d BAD_OPTIONS 3
+@d BAD_PROGRAM 4
+@<Globální deklarace@>=
+char *prog_name;
+int status;
+
+@ Základní rozvržení funkce |main|.
+@<Hlavní program@>=
+int main (int argc, char **argv)
+{
+ @<Lokální proměnné funkce |main|@>;
+ prog_name=argv[0]; status = OK;
+ @<Načtení parametrů příkazového řádku@>;
+ if (!silent) fprintf (stderr, BANNER);
+ @<Inicializace datových struktur@>;
+ @<Zpracování souborů@>;
+ return status;
+}
+
+@* 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 |@<Zpracování
+ souborů@>|). 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 |BANNER|, ani sumarizaci, ani varování,
+ při nichž není program předčasně ukončen. Všechny tyto výpisy
+ směřují do |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 |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.
+@<Pomocné funkce@>=
+static void printusage (void)
+{
+ fprintf(stderr,
+ "usage: vlna [opt] [filenames]\n"
+ " opt -f : filter mode: file1 file2 ... file1->file2\n"
+ " file1 ... file1->stdout\n"
+ " ... stdin->stdout\n"
+ " nofilter: file1 [file2 file3 ...] all are in/out\n"
+ " -s : silent: no messages to stderr\n"
+ " -r : rmbackup: if nofilter, removes temporary files\n"
+ " -v charset : set of lettres to add tie, default: KkSsVvZzOoUuAI\n"
+ " -x code : code for tie symbol, default: 7E, example -x C2A0\n"
+ " -m : nomath: ignores math modes\n"
+ " -n : noverb: ignores verbatim modes\n"
+ " -l : LaTeX mode\n"
+ " -w : web mode\n");
+}
+
+@ Proměnné |isfilter|, |silent|, |rmbackup|, |nomath|, |noverb|,
+|latex|, resp. |web| říkají, že je nastaven parametr \.{-f}, \.{-s},
+\.{-r}, \.{-m}, \.{-n}, \.{-l}, resp. \.{-w}. Proměnná |charset|
+ukazuje buď na implicitní skupinu znaků |charsetdefault|, nebo (při
+použití parametru \.{-v}) na text uvedený v příkazovém řádku.
+@<Globální deklarace@>=
+int isfilter=0, silent=0, rmbackup=0, nomath=0, noverb=0, web=0, latex=0;
+char charsetdefault[]="KkSsVvZzOoUuAI";
+char *charset=charsetdefault;
+
+@ String |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 |tiestrlen| bez ohledu na to, zda obsahuje nebo
+neobsahuje nulové znaky (C-čková konvence pro stringy není použita).
+@<Globální deklarace@>=
+unsigned char tiestr[MAXLEN];
+int tiestrlen;
+
+@ @<Načtení parametrů ...@>=
+tiestr[0] = '~';
+tiestrlen = 1;
+while (argc>1 && argv[1][0] == '-') {
+ if (argv[1][2] != 0) printusage (), exit (BAD_OPTIONS);
+ switch(argv[1][1]) {
+ case 'f': isfilter = 1; break;
+ case 's': silent = 1; break;
+ case 'r': rmbackup = 1; break;
+ case 'v': if (argc<2) printusage (), exit (BAD_OPTIONS);
+ argv++; argc--; charset = argv[1]; break;
+ case 'x': if (argc<2) printusage (), exit (BAD_OPTIONS);
+ argv++; argc--; settiestr(argv[1]); break;
+ case 'm': nomath = 1; break;
+ case 'n': noverb = 1; break;
+ case 'l': latex = 1; break;
+ case 'w': web = 1; break;
+ default: printusage (), exit (BAD_OPTIONS);
+ /* nezn\'am\'y parametr */
+ }
+ argc--; argv++;
+}
+
+@ Vyřešíme konverzi kódu zapsaného za parametrem \.{-x} na string |tiestr|.
+@<Pomocné funkce@>=
+static unsigned char hexnum(char c) {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ printusage (), exit (BAD_OPTIONS);
+}
+static void settiestr(char *s) {
+ int i, j;
+ i = strlen(s);
+ if ((i > 2*MAXLEN) || i%2 == 1) printusage (), exit (BAD_OPTIONS);
+ tiestrlen = i/2;
+ j = 0;
+ for (i=0; i<tiestrlen; i++) {
+ tiestr[i] = hexnum(s[j++]) << 4;
+ tiestr[i] += hexnum(s[j++]);
+ }
+}
+
+@* Zpracování souborů. Parametr |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}.
+@d MAXLEN 120
+@<Lokální proměnné funkce...@>=
+FILE *infile, *outfile;
+char backup[MAXLEN];
+int j;
+
+@ Definujeme funkci pro výpis chybového hlášení při neúspěšném otevření
+souboru.
+@<Pomocné funkce@>=
+static void ioerr (char *f)
+{
+ fprintf(stderr, "%s: cannot open file %s\n", prog_name, f);
+}
+
+@ Způsob zpracování souborů rozlišíme podle režimu daným přepínačem \.{-f}.
+@<Zpracování souborů@>=
+if (isfilter) @<Zpracování v režimu filter@> @/
+else @<Zpracování všech souborů příkazové řádky@>
+
+@ V režimu |isfilter==1| je další zpracování závislé na počtu souborů v
+příkazové řádce:
+\begitems
+* nula souborů -- vstup je |stdin| a výstup je |stdout|,
+* jeden soubor -- je vstupní, výstup je |stdout|,
+* dva soubory -- první je vstupní, druhý výstupní,
+* více souborů -- program skončí s chybou.
+\enditems
+@<Zpracování v režimu filter@>=
+{
+ if (argc > 3) printusage (), exit (BAD_OPTIONS) ;
+ infile = stdin; outfile = stdout;
+ if (argc >= 2) infile = fopen (argv[1], "r");
+ if (infile == NULL) ioerr (argv[1]), exit (IO_ERR);
+ if (argc == 3) outfile = fopen(argv[2], "wb");
+ if (outfile == NULL) ioerr (argv[2]), exit (IO_ERR);
+ if (argc >= 2) filename = argv[1];
+ else filename = NULL;
+ tie (infile, outfile);
+ if (outfile != stdout) fclose (outfile);
+ if (infile != stdin) fclose (infile);
+}
+
+@ V~režimu |isfilter==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.
+@<Zpracování všech souborů příkazové řádky@>=
+{
+ if (argc==1) printusage (), exit(BAD_OPTIONS);
+ while (argc>1) {
+ argc--; argv++;
+ @<Přejmenuj vstup |argv[0]| na |backup| a otevři jej jako |infile|@>;
+ if (infile == NULL) {
+ ioerr (argv[0]); continue;
+ }
+ outfile = fopen (argv[0], "wb");
+ if (outfile == NULL) {
+ ioerr (argv[0]);
+ rename (backup, argv[0]);
+ status = WARNING;
+ continue;
+ }
+ filename = argv[0];
+ tie (infile, outfile);
+ fclose (outfile), fclose (infile);
+ if (rmbackup) remove (backup);
+ }
+}
+
+@ Při |isfilter==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 |rmbackup==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.
+@<Přejmenuj vstup...@>=
+infile = NULL;
+j = strlen (argv[0]) - 1;
+if (j >= MAXLEN || argv[0][j] == '~') {
+ if (!silent) fprintf (stderr, "%s: the conflict of file name %s\n",
+ prog_name, argv[0]);
+}
+else {
+ strcpy (backup, argv[0]);
+ backup[j] = '~';
+ remove (backup);
+ j = rename (argv[0], backup);
+ if (j == 0) infile = fopen (backup, "r");
+}
+
+@* Patterny. Abychom mohli účelně definovat chování programu
+v~různých situacích, zavedeme datovou strukturu |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 |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 (|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á |charset|).
+Každá pozice má svůj přepínač (|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 |next|, poslední
+pozice má |next==NULL|. Stejně tak jednotlivé patterny budeme
+sestavovat do seznamů a budou rovněž zřetězeny ukazatelem |next|.
+
+Pattern kromě řetězu pozic obsahuje ukazatel na funkci (proceduru) |proc|,
+která se má vykonat v~případě, že testovaný řetězec vyhovuje patternu.
+
+@d ONE 1 /* flag: prave jeden vyskyt */
+@d ANY 2 /* flag: nula nebo vice */
+@d ONE_NOT -1 /* flag: prave jednou, znak nesmi byt v mnozine */
+@d ANY_NOT -2 /* flag: nula nebo vice, znak nesmi byt v mnozine */
+
+@<Globální deklarace@>=
+typedef struct PATITEM { /* jedna pozice patternu */
+ char *str; /* seznam znaku na teto pozici */
+ int flag; /* vyznam seznamu znaku */
+ struct PATITEM *next ; /* nasledujici pozice patternu */
+} PATITEM;
+typedef struct PATTERN { /* jeden pattern */
+ PATITEM *patt; /* ukazatel na prvni pozici */
+ void (*proc)(void); /* procedura spustena pri souhlasu patternu */
+ struct PATTERN *next ; /* nasledujici v seznamu vsech patternu */
+} PATTERN;
+
+@ Deklarujeme některé globální proměnné pro práci s~patterny. |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 |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 |lapi| budou na počátku samá |NULL| (to se při překladu inicializuje
+samo) a přemazání ukazatele na pozici konstantou |NULL| budeme považovat
+za zavření patternu. Vedle pole |lapi| souměrně udržujeme pole |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 |proc|
+patternu.
+
+|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 |normallist| a
+|commentlist| a aktivní |listpatt| má vždy jednu z~těchto dvou hodnot.
+
+Proměnné |lastpt| a |lastpi| použijeme pro budování řetězové struktury
+patternů.
+
+Proměnná |c| obsahuje právě testovaný znak ze vstupu (který se rovněž
+přepíše do bufferu |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 |BUFI|, abychom proceduru přepisu bufferu do
+výstupního proudu neaktivovali zbytečně často.
+@d MAXPATT 200 /* maximalni pocet patternu */
+@d MAXBUFF 500 /* velikost bufferu pro operace */
+@d BUFI 300 /* velikost stredniho zaplneni */
+@<Globální deklarace@>=
+PATITEM *lapi[MAXPATT]; /* pole ukazatelu na aktualni pozice */
+PATTERN *lapt[MAXPATT]; /* pole odpovidajicich ukazatelu na patterny */
+PATTERN *listpatt, *normallist, *commentlist, *pt, *lastpt=NULL;
+PATITEM *lastpi=NULL;
+char c; /* zrovna nacetny znak */
+char buff[MAXBUFF]; /* prechodny buffer */
+int ind; /* aktualni pozice prechodneho bufferu */
+
+@ 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 |buff| může obsahovat i nulové byty, které je třeba přepsat do výstupu.
+Na druhé straně buffer |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 |buffnz| osmibytový, začnu jej plnit od |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 |buffnz|[4].
+@<Globální deklarace@>=
+char buffnz[8];
+int inz;
+
+@ Nyní definujeme pomocné funkce |setpattern|, |setpi| a |normalpattern|.
+Tyto funkce alokují paměť pomocí standardní funkce |malloc|. Abychom mohli
+ohlídat případnou chybu při alokaci, budeme allokovat paměť zprostředkovaně
+pomocí funkce |myalloc|.
+@<Pomocné funkce@>=
+static void *myalloc (int size)
+{
+ void *p;
+ p = malloc (size);
+ if (p == NULL)
+ {
+ fprintf (stderr, "%s, no memory, malloc failed\n", prog_name);
+ exit (BAD_PROGRAM) ;
+ }
+ return p;
+}
+
+@ Funkce |setpattern| alokuje paměťové místo struktury |PATTERN| a napojí
+ji pomocí proměnné |lastpt| na už alokovaný řetěz patternů.
+Vrátí ukazatel na nově alokované místo. Jednotlivé pozice patternu se musí
+následovně alokovat pomocí |setpi|.
+@<Pomocné funkce@>=
+static PATTERN *setpattern (void (*proc)(void))
+{
+ PATTERN *pp;
+ pp = myalloc (sizeof (PATTERN));
+ pp->proc = proc;
+ pp->next = NULL;
+ pp->patt = NULL;
+ if (lastpt != NULL) lastpt->next = pp;
+ lastpt = pp;
+ lastpi = NULL;
+ return pp;
+}
+
+@ Funkce |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
+|patt| ve struktuře |PATTERN| a další byly provázány položkou |next| ve
+struktuře |PATITEM|. Poslední pozice má |next==NULL|.
+@<Pomocné funkce@>=
+static void setpi (char *str, int flag)
+{
+ PATITEM* p;
+ p = myalloc (sizeof (PATITEM));
+ p->str = str; p->flag = flag;
+ p->next = NULL;
+ if (lastpi == NULL) lastpt->patt = p;
+ else lastpi->next = p;
+ lastpi = p;
+}
+
+@ Připravme si půdu pro funkci |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á
+|flag=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 |"."|,
+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ů.
+@<Globální deklarace@>=
+char strings[512];
+int i;
+
+@ Inicializujeme toto pole (znak, nula, znak, nula, atd...).
+@<Inicializace datových struktur@>=
+for (i=0; i<256; i++) {
+ strings[2*i] = (char) i; strings[2*i+1] = 0;
+}
+
+@ Definujme funkci |normalpattern|.
+@<Pomocné funkce@>=
+static PATTERN *normalpattern (void (*proc)(void), const char *str)
+{
+ PATTERN *pp;
+ int j=0;
+ pp = setpattern (proc);
+ while (str[j]) {
+ if (str[j]=='.') {
+ j++;
+ if (str[j]!='.') {
+ setpi (blankscr, ANY);
+ continue;
+ }
+ }
+ setpi (&strings[(unsigned char)str[j]*2], ONE);
+ j++;
+ }
+ return pp;
+}
+
+@ Funkce |match|. Definujeme funkci, která na základě hodnoty znaku |c|
+(proměnná |c| je definována jako globální), a pozice patternu |p| (parametr
+funkce) vrátí informaci o tom, zda znak souhlasí s patternem. Záporná čísla
+|FOUND|, resp. |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 |c|.
+
+Pokud je |c| v množině znaků pro danou pozici |p->str|, bude |m==1|, jinak
+je |m==-1|. Pokud tímto číslem pronásobíme hodnotu |p->flag|, nemusíme
+větvení podle |p->flag| programovat dvakrát. Hodnoty |flag| jsou totiž
+symetrické podle nuly, např. |ANY==-ANY_NOT|.
+@d FOUND -1
+@d NOFOUND -2
+@<Pomocné funkce@>=
+static int match (PATITEM *p)
+{
+ int m;
+ if (strchr (p->str, c) != NULL) m = 1; /* Znak nalezen */
+ else m = -1; /* Znak nenalezen */
+ switch (m * p->flag) {
+ case ANY: return 0; /* Souhas, neni nutny posun */
+ case ONE: if (p->next == NULL) return FOUND;
+ return 1; /* Souhas, nutny posun o 1 */
+ case ONE_NOT: return NOFOUND; /* Nesouhlas */
+ case ANY_NOT: @<Vrať hodnotu podle následující...@>;
+ }
+ return 0; /* Tady bychom nikdy nemeli byt, return pro potlaceni varovani */
+}
+
+@ O kolik pozic je třeba se posunout a s jakým výsledkem zjistíme
+rekurzivním voláním funkce |match|.
+@<Vrať hodnotu podle následující pozice patternu@>=
+switch (m = match (p->next)) {
+case NOFOUND: return NOFOUND;
+case FOUND: return FOUND;
+default: return 1 + m;
+}
+
+@* Vlnkovací funkce.
+Nejprve připravíme globální deklarace pro \uv{vlnkovací} funkci |tie|.
+Funkce |tie| \uv{ovlnkuje} vstupní soubor |infile| a vytvoří soubor
+|outfile|. Při |silent=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é |filename|. Proměnná |numline| počítá řádky, proměnná
+|numchanges| sčítá změny, tj. počet doplněných vlnek.
+Proměnná |mode| nabývý některé z hodnot |TEXTMODE|, |MATHMODE|,
+|DISPLAYMODE| a |VERBMODE| podle stavu ve čteném textu.
+@d TEXTMODE 0
+@d MATHMODE 1
+@d DISPLAYMODE 2
+@d VERBMODE 3
+@<Globální deklarace@>=
+char *filename; /* jmeno zpracovavaneho souboru */
+long int numline, numchanges; /* pro zaverecnou statistiku */
+int mode;
+
+@ Nyní definujeme vlnkovací funkci |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
+(|listpatt==commentlist|) a mimo komentář (|listpatt==normallist|);
+@<Vlnkovací funkce |tie|@>=
+static void tie (FILE *input, FILE *output)
+{
+ int ap; /* ap je pocet otevrenych patternu */
+ register int k, m, n;
+ int ic;
+ PATTERN *pp;
+ PATITEM *pi;
+
+ @<Inicializace proměnných při startu funkce |tie|@>;
+
+ while (!feof(input)) {
+ if (ap == 0 && ind > BUFI && c !='\\') @<Vyprázdni buffer@>;
+ @<Otevři nové patterny@>; /* 1. 2. 2010: prohozene poradi */
+ if (ind >= MAXBUFF) {
+ fprintf (stderr, "Operating buffer overflow, is anything wrong?\n");
+ exit (BAD_PROGRAM);
+ }
+ if ((ic = getc(input)) == EOF) /* opravil Cejka Rudolf */
+ break;
+ buff[ind++] = c = ic;
+ if (c == 0) continue; /* 30. 4. 2009 */
+ if (inz>=8) {
+ for (inz=0; inz<4; inz++) buffnz[inz] = buffnz[inz+4];
+ inz=4;
+ }
+ buffnz[inz++] = c;
+ if (c == '\n') numline++, listpatt = normallist;
+ if (c == '%' && mode!=VERBMODE && buffnz[inz-2] != '\\') listpatt = commentlist;
+ @<Projdi otevřené patterny@>;
+ }
+ @<Vyprázdni buffer@>;
+ if (!web) checkmode (); /* zaverecna kontrola modu */
+ if (!silent) @<Tiskni závěrečnou zprávu@>;
+}
+
+@ @<Inicializace proměnných při ...@>=
+for (k=0; k<MAXPATT; k++) lapi[k] = NULL;
+c = '\n';
+buff[0] = 1; mode = ap = 0; ind = 1;
+for(inz=0; inz<4; inz++) buffnz[inz] = 0;
+inz = 4;
+numline = 1; numchanges = 0;
+mode = TEXTMODE;
+
+@ Při manipulaci s bufferem byl použit jeden trik. Veškeré načtené znaky
+začínají až od |buff[1]|, zatímco |buff[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 |buff[0]|, v některých situacích
+bychom se ptali, zda |buff[-1]=='\\'|, 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~|buffnz|, ale vlastnost dříve implementovanou v |buff| jsem ponechal beze změny.
+@<Vyprázdni buffer@>=
+{
+ fwrite (&buff[1], ind-1, 1, output);
+ ind = 1;
+}
+
+@ Při procházení otevřenými patterny posunujeme v poli |lapi| pozice
+jednotlivých patternů podle pokynů funkce |match|, případně pattern zavřeme
+a případně vyvoláme proceduru patternu.
+
+Některé patterny v poli |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.
+@<Projdi otevřené patterny@>=
+n = ap; k = 0;
+while (n) {
+ while (lapi[k]==NULL) k++; /* zastav se na prvnim ukazateli na pattern */
+ switch (m = match (lapi[k])) {
+ case FOUND: (*lapt[k]->proc)(); /* Pattern nalezen, spustit proceduru */
+ case NOFOUND: lapi[k] = NULL; /* Deaktivace patternu */
+ ap--; break;
+ default: while (m--) lapi[k] = lapi[k]->next; /* dalsi pozice patternu */
+ }
+ k++; n--;
+}
+
+@ 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 |FOUND| nebo |NOFOUND|. V takových případech ani nezanášíme ukazatel
+na pozici do pole |lapi|.
+@<Otevři nové patterny@>=
+pp = listpatt;
+if (c) while (pp != NULL) {
+ switch (m = match (pp->patt)) {
+ case FOUND: (*pp->proc)(); /* spustit proceduru */
+ case NOFOUND: break;
+ default: @<Vytvoř ukazatel na nový pattern a |break|@>;
+ }
+ pp=pp->next;
+}
+
+@ 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 |lapi| a tam uhnízdíme nový ukazatel
+na pozici v patternu.
+@<Vytvoř ukazatel na nový pattern...@>=
+pi = pp->patt;
+while (m--) pi = pi->next;
+n = ap; k = 0;
+while (n) {
+ if (lapi[k]==pi) break;
+ if (lapi[k++] != NULL) n--;
+}
+if (!n) {
+ k = 0;
+ while (lapi[k] != NULL) k++;
+ if (k >= MAXPATT) {
+ fprintf (stderr, "I cannot allocate pp, is anything wrong?\n");
+ exit (BAD_PROGRAM);
+ }
+ lapt[k] = pp; lapi[k] = pi; ap++;
+}
+
+@ Poslední věcí ve funci |tie| je tisk závěrečné statistiky zpracování.
+@<Tiskni závěrečnou zprávu@>=
+fprintf (stderr, "~~~ file: %s\t lines: %ld, changes: %ld\n",
+ filename, numline, numchanges);
+
+@* 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.
+
+@<Globální deklarace@>=
+char tblanks[] = " ~\t";
+char blanks[] = " \t";
+char blankscr[] = " \t\n";
+char tblankscr[] = " ~\t\n";
+char nochar[] = "%~\n";
+char cr[] = "\n";
+char prefixes[] = "[({~";
+char dolar[] = "$";
+char backslash[] = "\\";
+char openbrace[] = "{";
+char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+PATTERN *vlnkalist, *mathlist, *parcheck, *verblist ;
+
+@ Začneme definicí nejčastěji používaného patternu na vlnkování uvnitř
+řádku. Připomeňme, že opakované volání funkce |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
+|normallist|. Stejně tak opakované volání funkce |setpi| vytváří seznam
+pozic pro naposledy deklarovaný pattern.
+@<Inicializace datových struktur@>=
+vlnkalist = setpattern (vlnkain);
+setpi (tblankscr, ONE);
+setpi (tblanks, ANY);
+setpi (prefixes, ANY);
+setpi (charset, ONE);
+setpi (blanks, ONE);
+setpi (blanks, ANY);
+setpi (nochar, ONE_NOT);
+
+@ @<Inicializace proměnných při ...@>=
+listpatt = normallist = vlnkalist;
+
+@ Vložení vlnky znamená vykonat následující práci: Zapamatovat si znak za skupinou mezer
+(do proměnné |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é |z|. Dále se posuneme v bufferu vlevo přes všechny mezery, tabelátory
+(přesněji |blanks|) a přeskakujeme přitom všechny nulové byty. Index |ind| se zastaví
+na předložce. Posuneme jej doprava za předložku (++|ind|) a pokud tam je nulový byte a první znak
+|tiestr| není nulový, posuneme se až za tento nulový byte. Dále vložíme string |tiestr|,
+neboli vlnku. Nakonec připojíme zapamatovaný znak |p|, ovšem pokud před ním byla nula,
+vložíme ji ještě před znak |p|.
+@<Pomocné funkce@>=
+static void vlnkain(void)
+{
+ int i;
+ char p, z;
+ ind--;
+ p = buff[ind--];
+ z = buff[ind];
+ while (!buff[ind] || (strchr(blanks, buff[ind]) !=NULL)) ind--;
+ if (!buff[++ind] && tiestr[0]) ind++;
+ for (i=0; i<tiestrlen; i++) buff[ind++] = tiestr[i];
+ i = tiestrlen; /* nulu pred p vlozime, pokud je z==0 a */
+ if (!tiestr[0]) i--; /* pocet vlozenych znaku z tiestr */
+ if (!z && (i%2)) buff[ind++] = 0; /* je sudy */
+ buff[ind++] = p;
+ numchanges++;
+}
+
+@ Podobně pro tvorbu vlnky \uv{přes řádek} vytvoříme pattern a kód
+procedury.
+@<Inicializace dat...@>=
+setpattern (vlnkacr);
+setpi (tblankscr, ONE);
+setpi (tblanks, ANY);
+setpi (prefixes, ANY);
+setpi (charset, ONE);
+setpi (blanks, ANY);
+setpi (cr, ONE);
+setpi (blanks, ANY);
+setpi (nochar, ONE_NOT);
+
+@ 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ší.
+@<Pomocné funkce@>=
+static void vlnkacr(void)
+{
+ char p, z;
+ int i, j;
+ ind--;
+ p = buff[ind--];
+ z = buff[ind];
+ while (!buff[ind] || (strchr(blankscr, buff[ind]) !=NULL)) ind--;
+ i = ind; /* misto predlozky, kterou chceme vazat */
+ while (i >= 0 && (strchr(blankscr, buff[i]) == NULL)) i--;
+ j = i;
+ while (i >= 0 && (!buff[ind] || (strchr(blanks, buff[i]) != NULL))) i--;
+ if (i >= 0 && buff[i] == '\n') j = -1;
+ if (j >= 0) buff[j] = '\n';
+ else numline--;
+ if (!buff[++ind] && tiestr[0]) ind++;
+ for (i=0; i<tiestrlen; i++) buff[ind++] = tiestr[i];
+ i = tiestrlen;
+ if (!tiestr[0]) i--;
+ if (!z && (i%2)) buff[ind++] = 0;
+ buff[ind++] = p;
+ numchanges++;
+}
+
+@ Nyní vytvoříme patterny pro případy typu \.{\char92uv\char`\{v lese\char`\}}.
+@<Inicializace dat...@>=
+setpattern (vlnkain); /* na radku */
+setpi (tblankscr, ONE);
+setpi (backslash, ONE);
+setpi (letters, ONE);
+setpi (letters, ANY);
+setpi (openbrace, ONE);
+setpi (prefixes, ANY);
+setpi (charset, ONE);
+setpi (blanks, ONE);
+setpi (blanks, ANY);
+setpi (nochar, ONE_NOT);
+
+setpattern (vlnkacr); /* pres radek */
+setpi (tblankscr, ONE);
+setpi (backslash, ONE);
+setpi (letters, ONE);
+setpi (letters, ANY);
+setpi (openbrace, ONE);
+setpi (prefixes, ANY);
+setpi (charset, ONE);
+setpi (blanks, ANY);
+setpi (cr, ONE);
+setpi (blanks, ANY);
+setpi (nochar, ONE_NOT);
+
+
+
+@ 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...}}.
+@<Inicializace dat...@>=
+normalpattern (tielock, "\\TeX");
+setpi (blankscr, ONE);
+normalpattern (tielock, "\\LaTeX");
+setpi (blankscr, ONE);
+
+@ Procedura |tielock| obsahuje nečistý trik. Při provádění procedury je
+právě načten znak z |blankscr| a je uložen do |buff|. Testy na otevírání
+nových patternů pro tento znak teprve budou následovat a testují se na
+hodnotu proměnné |c|. Stačí tedy změnit hodnotu |c| a vlnkovací patterny se
+neotevřou.
+@<Pomocné funkce@>=
+static void tielock (void)
+{
+ c = 1;
+}
+
+@ 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.
+@<Pomocné funkce@>=
+static void printwarning (void)
+{
+ if (!silent)
+ fprintf (stderr,
+ "~!~ warning: text/math/verb mode mismatch, file: %s, line: %ld\n",
+ filename, numline - (c=='\n'?1:0));
+ status = WARNING;
+}
+
+@ 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.
+@<Inicializace datových ...@>=
+if (!nomath) {
+ mathlist = setpattern (onedollar);
+ setpi (dolar, ONE);
+ setpi (dolar, ONE_NOT);
+ if (latex) {
+ normalpattern (mathin, "\\begin.{math}");
+ normalpattern (mathout, "\\end.{math}");
+ }
+}
+
+@ @<Pomocné funkce@>=
+static void mathin (void)
+{
+ if (mode!=TEXTMODE) printwarning ();
+ mode = MATHMODE;
+ normallist = listpatt = mathlist;
+}
+static void mathout (void)
+{
+ if (mode!=MATHMODE) printwarning ();
+ mode = TEXTMODE;
+ normallist = listpatt = vlnkalist;
+}
+
+@ Při programování procedury |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.
+@<Pomocné funkce@>=
+static void onedollar (void)
+{
+ if (buffnz[inz-3]=='\\' || (buffnz[inz-3]=='$' && buffnz[inz-4]!='\\')) return;
+ if (mode==DISPLAYMODE) printwarning ();
+ else {
+ if (mode==TEXTMODE) mathin();
+ else mathout();
+ }
+}
+
+@ 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.
+@<Inicializace dat...@>=
+parcheck = setpattern (checkmode);
+setpi (cr, ONE);
+setpi (blanks, ANY);
+setpi (cr, ONE);
+
+@ @<Pomocné funkce@>=
+static void checkmode (void)
+{
+ if (mode!=TEXTMODE) {
+ printwarning ();
+ mode = TEXTMODE;
+ normallist = listpatt = vlnkalist;
+ }
+}
+
+@ 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.
+
+@<Inicializace dat...@>=
+if (!nomath) {
+ normalpattern (twodollars, "$$");
+ if (latex) {
+ normalpattern (displayin, "\\begin.{displaymath");
+ normalpattern (displayin, "\\begin.{equation");
+ normalpattern (displayout, "\\end.{displaymath");
+ normalpattern (displayout, "\\end.{equation");
+ }
+}
+
+@ @<Pomocné funkce@>=
+static void displayin (void)
+{
+ if (mode!=TEXTMODE) printwarning ();
+ mode = DISPLAYMODE; normallist = listpatt = parcheck;
+}
+static void displayout (void)
+{
+ if (mode!=DISPLAYMODE) printwarning();
+ mode = TEXTMODE; normallist = listpatt = vlnkalist;
+}
+static void twodollars (void)
+{
+ if (buffnz[inz-3]=='\\') return;
+ if (mode==DISPLAYMODE) displayout ();
+ else displayin ();
+}
+
+@ 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á).
+@<Inicializace dat...@>=
+if (!noverb) {
+ verblist = normalpattern (verbinchar, "\\verb");
+ setpi (blankscr, ANY);
+ setpi (blankscr, ONE_NOT);
+ normalpattern (verbin, "\\begtt");
+ if (latex) normalpattern (verbin, "\\begin.{verbatim");
+}
+if (web) {
+ normalpattern (verbin, "@@<");
+ normalpattern (verbin, "@@d");
+}
+if (!noverb) {
+ verboutlist[0] = setpattern (verbout);
+ setpi (verbchar, ONE);
+ verboutlist[1] = normalpattern (verbout, "\\endtt");
+ if (latex) verboutlist[2] = normalpattern (verbout, "\\end{verbatim");
+}
+if (web) {
+ verboutlist[3] = normalpattern (verbout, "@@ ");
+ normalpattern (verbout, "@@*");
+ normalpattern (verbout, "@@>|");
+}
+
+
+@ Procedura |verbinchar| se od \uv{společné} procedury |verbin| liší v
+tom, že zavede do stringu |verbchar| momentální hodnotu proměnné |c|.
+Proto druhý výskyt této hodnoty verbatim režim ukončí.
+@<Pomocné funkce@>=
+int prevmode;
+PATTERN *prevlist, *verboutlist[4];
+char verbchar[2];
+static void verbinchar (void)
+{
+ prevmode = mode;
+ verbchar[0] = c;
+ c = 1;
+ listpatt = normallist = verboutlist[0];
+ prevlist = listpatt->next;
+ listpatt->next = NULL;
+ mode = VERBMODE;
+}
+
+@ Při programování \uv{obecné} funkce |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).
+@<Pomocné funkce@>=
+static void verbin (void)
+{
+ int i;
+ i = 0;
+ prevmode = mode;
+ switch (c) {
+ case 't': i = 1; break;
+ case 'm': i = 2; break;
+ case '<': ;
+ case 'd': i = 3;
+ if (buffnz[inz-3]=='@@') return; /* dvojity @@ ignorovat */
+ break;
+ }
+ listpatt = normallist = verboutlist[i];
+ prevlist = listpatt->next;
+ if (c != '<' && c != 'd') listpatt->next = NULL;
+ mode = VERBMODE;
+}
+
+@ @<Pomocné funkce@>=
+static void verbout (void)
+{
+ if (mode!=VERBMODE) return;
+ if (web && buffnz[inz-2] == '@@' && buffnz[inz-3] == '@@') return;
+ mode = prevmode;
+ normallist->next = prevlist;
+ switch (mode) {
+ case DISPLAYMODE: normallist = listpatt = parcheck; break;
+ case MATHMODE: normallist = listpatt = mathlist; break ;
+ case TEXTMODE: normallist = listpatt = vlnkalist; break;
+ }
+}
+
+@ 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 |lastpt|.
+@<Inicializace dat...@>=
+lastpt = 0;
+commentlist = normalpattern (tieoff, "%.~.-");
+normalpattern (tieon, "%.~.+");
+
+@ @<Pomocné funkce@>=
+static void tieoff (void)
+{
+ normallist = NULL;
+}
+static void tieon (void)
+{
+ normallist = vlnkalist;
+}
+
+@ 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}).
+
+@* Rejstřík.
+
+