\"/
\"/ \"/    

Práce s terminály v Unixu

Luděk Matyska, ÚVT MU
Ročník IV - číslo 3, leden 1994
Citace: L. Matyska. Práce s terminály v Unixu. Zpravodaj ÚVT MU. ISSN 1212-0901, 1994, roč. IV, č. 3, s. 1-6.
Tematické zařazení: Hardware, Operační systémy
 předchozí číslo | následující článek 

Jedním z nepříjemných problémů, na který narazíme hned v počátcích práce v počítačové síti, je otázka emulace a nastavení charakteristik terminálů. Například přihlašujeme-li se ze svého PC prostřednictvím sítě na unixový server, může tento server po nás chtít zadat typ terminálu, který bude naše PC emulovat (tj. PC bude "předstírat", že není PC, ale terminál zadaného typu). Správná volba terminálu může být podstatná při práci s aplikačními programy (editory, tabulkovými kalkulátory, ...): špatná volba může naši práci i znemožnit. Tento problém je znám i uživatelům systému MS DOS v souvislosti s použitím obrazovkového driveru ansi.sys (případně dalšími).

Každý terminál1 výrobce vybaví určitými schopnostmi, které souhrnně určují jeho "inteligenci". Mezi nejdůležitější patří schopnost terminálu reagovat na přijímané speciální sekvence znaků a v závislosti na nich provádět určité činnosti, jako je např. smazání řádku, rolování zpět, nastavení kurzoru na určený řádek/sloupec a podobně. Je přirozené, že tyto vlastnosti mohou využívat aplikační programy k lepší a rychlejší práci s materiálem na obrazovce - např. zaslání jediného (a většinou velmi krátkého) příkazu pro smazání řádku na obrazovce (plus zaslání celé nové řádky, která se objeví na nejspodnějším řádku obrazovky) je podstatně rychlejší a snazší, než zaslání instrukcí k přepsání celé dolní části obrazovky. Pozitivní vliv výrazného snížení počtu dat zasílaných na terminál se přirozeně nejvíce projeví v situacích, kdy je terminál připojen kanálem s nepříliš vysokou propustností (např. přistupujeme-li k počítači přes modem s malou přenosovou rychlostí).

Při diskusi vlastností terminálů musíme uvažovat následující tři okruhy:

  1. Znaky vysílané klávesnicí při stisknutí jednotlivých kláves.
  2. Reakce terminálu na znaky posílané počítačem.
  3. Okamžité nastavení určitých charakteristik terminálu.

Stiskneme-li na klávesnici související s terminálem nějakou klávesu, je výsledkem odeslání určitého znaku (určitých znaků) do počítače. Klávesnice připojená přímo k počítači (konzola) vysílá většinou tzv. scan-kódy (snímané kódy), což je zhruba řečeno informace o čísle stisknuté klávesy (většinou zadaná souřadnicemi) a rovněž informace o tom, zda se jedná o stisknutí nebo naopak uvolnění klávesy. Klávesnice klasických terminálů ve většině případů vysílá přímo příslušný znak (většinou v ASCII kódu) či posloupnost znaků (většinou uvedenou tzv. escape znakem - hovoříme proto o escape sekvencích2) - jedná se především o funkční klíče, šipky a ostatní pomocné klávesy. Aby aplikační programy nemusely rozlišovat mezi klávesnicí přímo připojenou k počítači a klávesnicí (vzdáleného) terminálu, vsouvá operační systém mezi znaky vysílané klávesnicí počítače a aplikace speciální program - filtr - jehož úkolem je převést scan-kódy na znaky zvoleného kódování (a na příslušné escape sekvence). Některé moderní terminály (např. hp 700/60) disponují i režimem, ve kterém vysílají rovněž scan-kódy (a tím mohou být použity i k práci s programy, které obcházejí výše zmíněný filtr a vyžadují přístup přímo ke scan-kódům). Většina operačních systémů (včetně Unixu) umožňuje vložit mezi terminál a aplikační program další speciální program - filtr, který znaky (jejich posloupnosti) vysílané terminálem převádí na jiné (takto je například zajištěn vstup znaků s diakritickými znaménky z běžné klávesnice)3.

Hlavní část "inteligence" terminálu spočívá v možnosti ovlivňovat jeho vnitřní stav znaky na něj zasílanými. Přijme-li terminál znak od počítače, nejběžnější reakcí je zobrazení tohoto znaku na obrazovce. Přesto prakticky všechny terminály (kromě těch "nejhloupějších" - dumb) přechází po přijetí určitého znaku (tzv. escape znaku) do kontrolního režimu, v němž následující znaky tvoří příkazy terminálu - znaky tedy nejsou zobrazovány, ale mění stav terminálu (typickým příkladem je escape sekvence, která posune kurzor na zadaný řádek/sloupec obrazovky; požadovaná poloha kurzoru tvoří součást příkazu).

Třetí oblastí jsou pak okamžité vlastnosti terminálu. Jedná se především o nastavení konkrétních hodnot určitých volitelných parametrů. Typickým příkladem je nastavení, zda terminál zobrazuje znaky zadávané přímo z klávesnice či nikoliv (tzv. "echo") - potlačení se využívá např. při zadání hesla. Je přirozené, že každý terminál může pracovat v režimu no-echo - je tedy úlohou operačního systému (a aplikačních programů) vybrat z obou možností tu, která je v konkrétním okamžiku potřebná.

Tento článek je věnován především otázkám identifikace terminálu - postupům, jak operačnímu systému a aplikačním programům sdělit, z jakého terminálu s počítačem komunikujeme a jaké vlastnosti ("inteligenci") tento terminál má. Při té příležitosti se i stručně zmíníme o nastavení okamžitých charakteristik terminálů a prostředcích jejich změny. Veškerá naše diskuse se bude týkat pouze operačního systému Unix, některé ze základních principů jsou však obecněji platné.

Popis vlastností

Nejtypičtějším uživatelem informací o vlastnostech terminálu jsou editory. Zadáme-li v používaném editoru příkaz pro smazání textu od polohy kurzoru do konce aktuálního řádku, má editor v principu tři možnosti, jak tento příkaz provést: (i) přepíše celý řádek (od levého do pravého konce obrazovky), (ii) přepíše zbytek řádku mezerami, (iii) pošle speciální příkaz, funkčně ekvivalentní předchozímu příkazu (ovšem k přepsání dojde pouze lokálně v terminálu, po komunikační lince není nutno příslušný počet mezer přenést). Je zřejmé, že poslední varianta je obecně nejvýhodnější, k jejímu provedení ovšem musí editor vědět, zda takový příkaz pro terminál existuje a v kladném případě musí znát jeho syntax. Editory (ale i jiné programy) mohou využít i dalších vlastností terminálu - smazání celé nebo části obrazovky, zvýraznění textu, nezávislé nastavení kurzoru, pohyb zpět a další.

Jednotlivé terminály se ovšem velmi liší jak v rozsahu těchto vlastností, tak i v konkrétním tvaru jednotlivých příkazů. Výše uvedený příkaz smazání textu od polohy kurzoru do konce řádku má např. následující tvary4:

Problém spojený s uchováním informací o stovkách již existujících terminálů (a možností doplňovat informace o terminálech nových) vedl v rámci operačního systému Unix k návrhu unifikovaného rozhraní, v jehož rámci jsou všem (potenciálním) vlastnostem přiřazeny pevné kódy. Aplikační programy pak přečtením hodnoty příslušného kódu okamžitě zjistí, nejen zda příslušnou vlastnost zrovna používaný terminál má, ale též jaká je syntax příslušného příkazu. Definice terminálu je pak tvořena posloupností příkazů kód=řetězec, kde řetězec je posloupnost znaků tvořících příslušný příkaz5. Je-li kód pro výše uvedený příkaz - smazání do konce řádku - ce, pak pro výše uvedené terminály má příslušná položka definice vlastností tvar:

Definice jednotlivých terminálů jsou pojmenovány a přes toho jméno zpřístupněny uživateli.

Databáze termcapterminfo

Protože svět není úplně ideální, používá se v rámci operačního systému Unix dvou přístupů k vytvoření tohoto rozhraní. Oba přístupy vycházejí ze stejných principů (uvedených výše), liší se však ve jménech jednotlivých kódů vlastností a rovněž v podobě, v jaké jsou vlastnosti terminálů zpřístupněny aplikačním programům.

Historicky starší je databáze popisu vlastností terminálů termcap, vyvinutá jako součást distribuce BSD Unixu. S touto databází je možno se setkat ve všech instalacích Unixu, vycházejících z verze BSD (a většiny verzí SVR4). Databáze termcap je tvořena jedním (rozsáhlým) souborem, který je editovatelný běžnými editory. Tento soubor - jména termcap - bývá uložen buď v adresáři /etc nebo adresáři /usr/share/lib. Přidávání nových položek - definic nových terminálů - je možno realizovat prostou editací souboru termcap.

Při vývoji definice nového terminálu není vždy vhodné zasahovat přímo do souboru termcap (a pro běžného uživatele to ani nepřichází do úvahy vzhledem k ochraně tohoto souboru). V takovém případě je možno vytvořit nový soubor s popisem vlastností a jeho jméno uvést v shellové proměnné TERMCAP (jméno musí být absolutní, tj. musí začínat lomítkem; v opačném případě se systém domnívá, že obsahem proměnné TERMCAP je přímo řetězec definice terminálu)6. Soubor specifikovaný v proměnné TERMCAP je prohledáván dříve než standardní termcap, přitom se vždy používá první nalezená definice (takto lze modifikovat a opravovat položky v termcap i bez možnosti modifikace standardní databáze).

Všechny Unixy vycházející z AT&T systému V používají databázi terminfo (poprvé použita v systému V Release 2). Hlavní rozdíl oproti termcapu spočívá v binární podobě, ve které je tato databáze v systému uložena. Na rozdíl od jednoho dlouhého souboru s definicemi všech terminálů jsou definice jednotlivých terminálů uloženy jako samostatné binární soubory (s názvem terminálu). V systému je vždy přítomen podstrom terminfo/[a-zA-Z0-9]/jméno_terminálu (většinou pod /usr/lib), v němž jsou uloženy definice jednotlivých standardních terminálů (jednotlivé jednoznakové podadresáře jsou používány pouze pro rychlejší přístup k definicím terminálů - ty jsou sdružovány podle prvního písmene názvu). Definice nových terminálů pro databázi terminfo je možno vytvořit v běžné textové podobě (analogicky jako u databáze termcap), ovšem zpřístupnění definice je provedeno terminfo kompilátorem tic, který textovou podobu definice terminálu převede do binární a tu uloží do správného podadresáře podstromu terminfo.

Vývoj vlastních definic v systému terminfo je poněkud komplikován nutností překladu do binární formy. Je-li nastavena shellovská proměnná TERMINFO, je její hodnota chápána jako jméno adresáře, který představuje kořen stromu alternativních definic terminfo7. V tomto případě kompilátor tic umisťuje nově generované definice do tohoto stromu a při přístupu k definici terminálu se tento podstrom prohledává dříve než podstrom standardní. Máme-li k dispozici termcap definici vlastností terminálu, můžeme programem captoinfo vytvořit odpovídající terminfo popis (tento program je zvlášť užitečný při přechodu z Unixu využívajícího termcap databázi na nový systém, založený na terminfo - všechny nové definice a úpravy starých lze jednoduše a v podstatě automaticky převést do nového systému).

Databáze definic vlastností terminálů obsahují v běžných instalacích operačního systému Unix několik set definic terminálů (např. databáze termcap v operačním systému SCO Unix System V Release 3.2.4 obsahuje přes 400 definic).

Konzola počítače

Ne vždy dobře specifikovány zůstávají vlastnosti systémové konzoly. Za starých časů systémovou konzolu tvořil opět terminál (často s hard-copy výstupem), jehož vlastnosti byly pevně dány výrobcem (a jimž odpovídal příslušný popis v databázi vlastností). Máme-li však Unix instalovaný na osobním počítači či pracovní stanici s přímo připojenou obrazovkou a klávesnicí, jsou vlastnosti terminálu/konzoly definovány implicitním emulačním programem (který zajistí, že obrazovka a klávesnice se vůči aplikačním programům tváří jako jakýkoliv jiný terminál). Emulace terminálu/konzoly je jedním z problematických záležitostí Unixů na osobních počítačích a pracovních stanicích - jednotliví výrobci jako by považovali za svou povinnost definovat ve "svém" Unixu jiný terminál než konkurence. Jako by to nestačilo, jsou tyto "terminály" pouze podobné skutečným terminálům. Důsledkem je, že databáze termcap (terminfo) na Unixu A neobsahuje definici vlastností konzoly Unixu B. Jediným východiskem z této nepříjemné situace je přenesení informace o vlastnostech konzoly Unixu A do příslušné databáze vlastností terminálu Unixu B. Zvlášť zrádné je v této souvislosti spoléhat na kompatibilitu jednotlivých definic pseudoterminálů: např. konzola v SCO Unixu je definována jako tzv. ansi terminál (tj. terminál odpovídající definici Amerického standardizačního institutu). Přihlásíme-li se však z SCO Unixu do SunOSu a uvedeme-li jako typ terminálu ansi, získáme podstatně omezenější definici, která zdaleka neumožňuje využít všech skutečných schopností konzoly SCO Unixu (např. nejsou k dispozici šipky pro pohyb kurzoru).

Zatímco převzít definici terminálu z databáze termcap není v principu žádný problém (editor postačí), situace může být poněkud komplikovanější v případě, máme-li k dispozici pouze binární podobu terminfo databáze. V této situaci můžeme použít program infocmp a s jeho pomocí převést binární formu zpět do textové podoby. Program infocmp je schopen převést binární terminfo záznam jak do textového terminfo záznamu, tak i do ekvivalentního záznamu termcap (v případech, kdy není automatický převod možný, je na tuto skutečnost ve vygenerovaném souboru upozorněno - obecně však lze položku vygenerovanou programem infocmp vždy přímo využít v termcap)8. Vzhledem k unifikované podobě terminfo databáze je možno binární položky přenést ze systému na systém a zpětně přeložit na tom, kde máme program infocmp k dispozici9.

Nastavení terminálu

Součástí přihlášení uživatele do systému je i určení typu terminálu, ze kterého k přihlášení došlo. Tato informace je poté uchována jako hodnota shellové proměnné TERM a používána všemi aplikačními programy10. Protože součástí nastavení terminálu bývá i jeho inicializace (aby se předešlo problémům spojeným s pozůstatky z nastavení předchozím uživatelem), nevystačí se většinou s prostým přiřazením shellové proměnné.

Pro usnadnění činností spojených s inicializací terminálu a nastavením shellové proměnné (shellových proměnných při použití termcap) byl vyvinut program tset. Tento program je součástí většiny instalací AT&T Unixů a řady Unixů vycházejících z BSD distribuce, není však součástí definice SVID3 rozhraní a konkrétní verze operačního systému Unix jej nemusí obsahovat (v tom případě je buď k dispozici nějaká jiná varianta nebo je třeba činnosti zprostředkované programem tset realizovat "ručně"). Program tset je většinou součástí inicializace uživatelského prostředí a je možno jej nalézt v souborech .profile, .login či .cshrc. Program tset má dvě základní funkce: (i) zjistit typ používaného terminálu a na základě této informace (ii) terminál inicializovat (a přitom pomoci nastavit shellové proměnné).

Program tset má pro zjištění typu terminálu k dispozici klíč

     -m prototyp:terminál

Program prohledává soubor definic vlastností linek vedoucích do počítače (a to jak fyzických, tak i virtuálních, souvisejících s připojením prostřednictvím počítačové sítě) - jedná se o soubor ttytype v AT&T Unixu System V, případně soubor ttytabs v BSD Unixu - a hledá položku odpovídající lince (kanálu), přes kterou je relace vedena. Je-li příslušná položka identická s prototyp, pak je terminál nastaven na terminál11. Vyčerpají-li se všechna opakování klíče -m, aniž by se podařilo nalézt vhodnou položku prototyp, je vybrána položka ze souboru ttytype. Prázdná položka prototyp je definitoricky identická s libovolným prototypem v ttytype (ttytabs) - tímto způsobem lze tedy vybrat automaticky terminál odlišný od toho, který je uveden v ttytype (ttytabs).

Tento způsob poloautomatického zjištění typu terminálu je postačující v případě použití terminálů připojených pevnými linkami. Pokud ovšem s operačním systémem komunikujeme prostřednictvím počítačové sítě (a to je v současné době převažující způsob), je pro každou relaci vybírán virtuální kanál, přičemž neexistuje žádný vztah mezi místem, odkud spojení navazujeme, a přiděleným kanálem. Položky v souboru ttytype (ttytabs) pro tyto kanály obsahují pouze generický popis network, kterému přirozeně neodpovídá žádný reálný terminál. V tomto prostředí nabízí program tset možnost poloautomatického dotazu. Předchází-li poli terminál v klíči -m znak otazník, je uživateli položen dotaz, zda komunikuje prostřednictvím uvedeného terminálu. Uživatel má pak možnost zadat interaktivně jiný terminál. Uvedením vhodné defaultní hodnoty (odpovídající terminálu, ze kterého se hlásíme nejčastěji) pak omezuje nutnou interakci při nastavení typu terminálu na minimum.

Hlásíme-li se na počítač s Unixem z jiného Unixovského počítače, pak je možno převzít právě používanou hodnotu shellové proměnné TERM a použít ji v polích prototypterminál programu tset. V tomto případě pak dále vzrůstá pravděpodobnost, že nabídnutý default bude odpovídat právě typu použitého terminálu.

V této souvislosti je třeba upozornit na rozpory v referenčních příručkách a rovněž na rozdíly v chování jednotlivých verzí Unixu. Podle referenčních příruček (manuálových stránek) předává hodnotu shellové proměnné TERM pouze příkaz vzdáleného přihlášení rlogin. V reálných systémech (SCO Unix SVR3.2.4, BSD/386, Unix SVR4 firmy ICL, SunOS 4.1.3) však předává aktuální hodnotu shellové proměnné TERM i příkaz telnet (a v některých případech - SCO Unix - pouze tento příkaz předá hodnotu proměnné TERM správně). Další rozdíl je možno pozorovat v reakci na nastavení prototypu v klíči -m v situaci, kdy pro příslušný kanál je v tabulce ttytype (ttytabs) uvedena položka network. Operační systémy SunOS a BSD v tomto případě považují jakoukoliv hodnotu v klíči -m za korektní, zatímco v systému SCO Unix tomu tak není.

Na závěr je možno uvést doporučený tvar příkazu tset v situaci, kdy se do systému hlásíme především na konzole nebo sítí:

     eval 'tset -m C:C -m :?$TERM -s -r -Q'

kde C je typ terminálu, který emuluje konzola počítače (význam uvedených klíčů viz příslušná manuálová stránka - v Unixu se vypíše zadáním man tset).

Změna nastavení

Vybereme-li správný typ terminálu a nastavíme-li shellové proměnné, máme většinu práce s terminálem hotovu a můžeme se již jen těšit z vlastního obcování s počítačem. Případné změny některých okamžitých (měnitelných) nastavení terminálu by už za nás měly obstarávat aplikační programy. Složitost světa však tento obrázek poněkud komplikuje. Některé aplikační programy končí havárií a zanechávají nás před nesprávně fungujícím terminálem: terminál nezobrazuje žádné znaky, není možno vložit příkaz, výstup z počítače není korektně zakončen návratem kurzoru, ...

Aktuální nastavení volitelných charakteristik terminálu je možno zjistit programem stty. Zavoláme-li jej s klíčem -a, získáme přehled o hodnotách všech nastavitelných parametrů terminálu a komunikační linky, přes kterou je připojen. Zatímco informace o rychlosti linky je při připojení přes síť již poněkud neaktuální (ale stále zůstává velmi podstatnou pro spojení s terminálem přes sériovou linku - nastavení na lince musí být identické s lokálním nastavením terminálu), na významu nabývají např. pole rowscolumns, definující velikost okna terminálu v počtu řádků a sloupců (tato informace je velmi důležitá při komunikaci prostřednictvím emulátorů virtuálních terminálů, jako je xterm, jejichž velikost lze měnit i v průběhu spojení12).

Příkazem stty je možno změnit i nastavení tzv. interrupterase znaků, tj. znaku, který vyvolá přerušení běhu programu (DEL na SCO Unixu, ^C na většině ostatních), a znaku způsobujícího vymazání znaku před kurzorem (^h nebo ^?).

V případě, že se terminál dostane do nedefinovaného stavu (např. proto, že byl násilně ukončen aplikační program, který přestavil charakteristiky linky a/nebo terminálu), přijde k užitku příkaz

     stty sane

který terminál a linku nastaví do určitého jasně definovaného stavu13. Protože aplikační program mohl předefinovat i chování klávesy enter, je třeba příkaz stty sane "obložit" namísto stisku klávesy enter znaky ^M (control-M). Znak ^M nelze předefinovat; vždy představuje přechod na nový řádek.

Závěr

Pro rozsáhlost problematiky nebylo samozřejmě možno popisovanou problematiku zpracovat vyčerpávajícím způsobem. Tento článek si proto kladl za cíl pouze seznámit přehledným způsobem s celou problematikou a naznačit, jakým způsobem je vhodné při manipulaci s terminály postupovat. Uvedené informace by měly být postačující i k tomu, aby se uživatelé dokázali vypořádat s běžnými potížemi spojenými s nesprávnou volbou terminálu či jeho nesprávným nastavením.

Zájemci o podrobnější seznámení se s databázemi vlastností terminálů a s příkazy spojenými s nastavením a prací s terminály by se měli seznámit (pročtením v referenční příručce či manuálových stránkách) s pojmy termcap, terminfotermio, s příkazy tset, tput, tty, stty a případně i s knihovnou curses.

setting
1 Pod tímto pojmem budeme v dalším rozumět jak skutečný terminál - tj. samostatnou obrazovku s klávesnicí, jako je např. vt100 či Wyse 60 - tak i konzolu počítače, tj. klávesnici a obrazovku přímo připojené k počítači.
... zpět do textu
2 Escape znamená opuštění (standardní interpretace znaků). Na většině klávesnic je k dispozici znak [Esc], který je též nejčastějším escape znakem ve zmíněných sekvencích.
... zpět do textu
3 Tento filtr je možno vsunout i mezi konzolu a aplikační program - v tomto případě pak mezi konzolou a programem stojí filtry dva: první převádí scan-kódy do znaků a druhý tyto znaky přemapovává.
... zpět do textu
4 Znak [Esc] je znak Esc (dekadicky 27 v ASCII tabulce).
... zpět do textu
5 V některých případech může být část =řetězec vynechána, jedná-li se pouze o informaci, zda uvedenou vlastnost terminál má či nikoliv (např. zda se jedná o terminál s automatickým výstupem na papír (tzv. hard-copy terminál).
... zpět do textu
6 V některých BSD systémech je pro stejný účel možno použít i soubor .termcap v domovském adresáři - jeho obsah je systémem chápán jako "soukromá" termcap databáze.
... zpět do textu
7 Na rozdíl od hodnoty proměnné TERMCAP nemusí jít v tomto případě o absolutní jména adresáře, protoľe nehrozí možnost záměny a nevhodné interpretace.
... zpět do textu
8 V některých systémech je k dispozici program untic s obdobnou funkcí.
... zpět do textu
9 Jediný problém tvoří přirozeně potenciální nekompatibilita samotného uložení binárních dat na rozdílných počítačích - nelze proto přenášet přímo položky terminfo např. ze Sunů na počítače s procesorem Intel x86.
... zpět do textu
10 V systémech používajících databázi termcap bývá definice terminálu uložena do shellové proměnné TERMCAP a takto zpřístupněna bez nutnosti jejího opakovaného načítání z rozsáhlé databáze.
... zpět do textu
11 Součástí položky prototyp může být i test na rychlost spojení (v baudech), takže je možno vybrat terminál podle toho, na jakou rychlost je právě komunikační linka nastavena.
... zpět do textu
12 Právě neexistence polí rowscolumns v SCO Unixu způsobuje problémy při komunikaci s tímto systémem prostřednictvím xterm virtuálních terminálů: aplikace v SCO Unixu obecně neumí reagovat na změnu velikosti okna xterm, protože není pevně definováno rozhraní, přes které by si tuto informaci mohly zjistit (program vi jako jeden z mála je schopen na změnu okna zareagovat).
... zpět do textu
13 Je ovšem třeba upozornit, že různé typy Unixů definují příčetnost sane různě. Musíme-li uvedený příkaz používat častěji, vyplatí se napsat si malý shellovský program, který po vydání příkazu sane upraví nastavení podle našich skutečných zvyklostí.
... zpět do textu
Zpět na začátek
ÚVT MU, poslední změna 14.11.2011