M ASARYKOVA UNIVERZITA FAKULTA INFORMATIKY
}
w A| y < 5 4 23 1 0 / -. , )+ ( %&' $ # !"
Æ
UTF-8 pro Midnight Commander ˇ BAKALÁ RSKÁ PRÁCE
Rostislav Beneš
Brno, 2008
Prohlášení Prohlašuji, že tato bakaláˇrská práce je mým puvodním ˚ autorským dílem, které jsem vypracoval samostatnˇe. Všechny zdroje, prameny a literaturu, které jsem pˇri vypracování používal nebo z nich cˇ erpal, v práci rˇádnˇe cituji s uvedením úplného odkazu na pˇríslušný zdroj.
Vedoucí práce: Mgr. Jan Kasprzak iii
Shrnutí Ve své práci se vˇenuji problematice modifikací starších aplikací pro souˇcasné prostˇredí založených na znakové sadˇe Unicode kódované v UTF-8. Konkrétnˇe se jedná o konzolového správce souboru˚ Midnight Commander. Pro nˇej sice již podobná úprava existuje, ale nedostaˇcuje zcela všem požadavkum ˚ a nestala se souˇcástí základní verze programu. Její pˇrijetí se ani ve stávajícím stavu neplánuje. Muj ˚ cíl tak spoˇcívá ve tvorbˇe lepší modifikace nejlépe tak, aby byla vývojáˇri pˇrijata a zaˇclenˇena do oficiálního zdrojového kódu aplikace.
v
Klíˇcová slova Midnight Commander, mc, UTF-8, S-Lang, GLib, Unicode, kódování, terminál, správce souboru˚
vii
Obsah 1 Úvod . . . . . . . . . . . . . . . . . . . . . . 1.1 Midnight Commander . . . . . . . . . 1.2 UTF-8 . . . . . . . . . . . . . . . . . . . 2 Pˇrehled situace . . . . . . . . . . . . . . . . 2.1 Zobrazení . . . . . . . . . . . . . . . . ˇ ezce v UTF-8 . . . . . . . . . . . . . 2.2 Retˇ 2.2.1 Velikost znaku . . . . . . . . . 2.2.2 Porovnávání . . . . . . . . . . . 2.2.3 Vyhledávání . . . . . . . . . . . 2.3 Puvodní ˚ UTF-8 patch . . . . . . . . . . 3 Úpravy pro UTF-8 . . . . . . . . . . . . . . 3.1 Výbˇer základní metody . . . . . . . . . 3.2 Jednotlivé úpravy . . . . . . . . . . . . 3.2.1 mc-00-slang . . . . . . . . . 3.2.2 mc-01-api . . . . . . . . . . . 3.2.3 mc-02-hotkey . . . . . . . . . 3.2.4 mc-03-button . . . . . . . . . 3.2.5 mc-06-input . . . . . . . . . 3.2.6 mc-10-buttonbar . . . . . . 3.2.7 mc-11-panel . . . . . . . . . 3.2.8 mc-13-file . . . . . . . . . . 3.2.9 mc-15-dialog . . . . . . . . . 3.2.10 mc-20-dir . . . . . . . . . . . 3.2.11 mc-26-help, mc-27-hint . 3.2.12 mc-29-vfs . . . . . . . . . . . 3.2.13 mc-30-view . . . . . . . . . . 3.2.14 mc-40-ncurses . . . . . . . . 3.2.15 mc-fullwidth-buttonbar . 3.2.16 Ostatní úpravy . . . . . . . . . 3.3 Aplikace . . . . . . . . . . . . . . . . . 4 Závˇer . . . . . . . . . . . . . . . . . . . . . . A Popis rozhraní pro rˇetˇezce . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3 3 4 7 7 8 8 8 8 9 13 13 14 14 14 16 17 17 17 18 18 18 19 19 19 20 23 23 23 23 25 29
1
Kapitola 1
Úvod V souˇcasnosti pˇrevládá tendence uchovávat texty ve znakové sadˇe Unicode [2], nejˇcastˇeji kódovanou v UTF-8 nebo UTF-16. Mnoho starších aplikací si však s novými kódováními neporadí a pokud se nedoˇckají zásadních úprav, pˇrestanou být zˇrejmˇe cˇ asem používány. Pˇresnˇe do této kategorie spadá i program Midnight Commander [4] (dále jen mc). Sice pro nˇej existuje neoficiální úprava, ale ta zatím nedosahuje dostateˇcné úrovnˇe pro zaˇclenˇení do hlavního vývojového stromu. Nabízí se mi tedy volba mezi dvˇema variantami. Bud’ pokracˇ ovat ve stávající úpravˇe nebo napsat vlastní modifikaci úplnˇe od zaˇcátku. Základní funkcionalita mc by však mˇela zustat ˚ zachována v puvodní ˚ podobˇe. Po dokonˇcení práce kontaktuji vývojáˇre mc a seznámím je s výsledky. V ideálním pˇrípadˇe bude moje práce zaˇclenˇena pˇrímo do zdrojového kódu programu. V následujících cˇ ástech pˇriblížím více oblasti, kterými se ve své práci zabývám.
1.1
Midnight Commander
Program mc patˇrí do rodiny správcu˚ souboru˚ se dvˇema panely. Pˇredstavuje asi nejznámˇejšího zástupce této kategorie aplikací používaných v GNU operaˇcních systémech. Vývoj mc zapoˇcal v roce 1994 a cílem bylo napodobit dˇríve populární Northon Commander, urˇcený pro operaˇcní systém DOS, a jemu podobné aplikace. Stejnˇe jako jeho vzor bˇeží jen v textových terminálech. Jak zaˇrazení programu napovídá, jeho základní funkce spoˇcívá v procházení adresáˇru˚ a zobrazení jejich obsahu. Dále pak zprostˇredkovává základní operace jako kopírování a pˇresun souboru˚ mezi adresáˇri. Provede je ovšem jen nad soubory, jejichž jména vyhovují zadanému regulárnímu výrazu. U jmen cílových souboru˚ pˇrípadnˇe zmˇení velikost písmen a to bud’ všech nebo jen prvního. Napˇríklad maska cíle \L\u* zaruˇcí, že jméno souboru zaˇcne velkým písmenem a pokraˇcovat bude jen malými. Na požádání soubory taktéž smaže nebo k vybranému souboru vytvoˇrí symbolický odkaz. Uživateli vypíše vlastníka oznaˇceného souboru a také pˇríslušnou skupinu i s oprávnˇeními a obsahuje i nástroje pro editaci tˇechto údaju. ˚ Pˇri zobrazení obsahu adresáˇre se jeho položky seˇradí podle zvolené vlastnosti jako jméno, pˇrípona, velikost cˇ i typ. Pokud tˇrídí podle jména, umožnuje ˇ upˇrednostnit velká písmena pˇred malými. Pro rychlejší a snadnˇejší orientaci v adresáˇrích s velkým množstvím souboru˚ nabízí vyhledání konkrétního souboru podle zaˇcátku jeho jména. Panel nemusí obsahovat jen položky z jednoho konkrétního adresáˇre, ale i seznam souboru˚ získaných z výstupu spuštˇeného externího pˇríkazu. Tato funkce se nazývá externí panelizace. Aplikace mc tak napˇríklad prezentuje výsledky hledání pˇríkazu find. Program mc nepˇristupuje k souborum ˚ pˇrímo pˇres systémové funkce, ale místo toho pracuje se soubory pˇres vlastní rozhraní nazvané Virtual File System (dále jen VFS). To dovoluje 3
1. Ú VOD pracovat nejen s lokálními soubory, ale i pˇripojit se k FTP nebo SSH serverum. ˚ VFS zpˇrístupnuje ˇ také pˇrímo obsah souboru, ˚ jenž v sobˇe uchovávají další soubory. Mezi takové soubory patˇrí ruzné ˚ archivní formáty jako tar, zip a rar, dále pak výsledky pˇríkazu diff a nebo balíˇcky rpm. Implementace VFS se pokouší minimalizovat závislost na mc a zdrojový kód VFS je kompletnˇe oddˇelen, ale i tak potˇrebuje nˇekolik pomocných funkcí nacházejících se ve zdrojových kódech mc. Aplikace mc v sobˇe integruje jednoduchý prohlížeˇc textových souboru, ˚ který podporuje bud’ prosté textové soubory nebo nroff 1 formátovaný text používaný pˇri zobrazení manuálových stránek. Souˇcasnˇe jej však lze pˇrepnout do jednoduchého hexadecimálního editoru. V obou režimech prohlížeˇc dovoluje v souboru vyhledávat rˇ etˇezce, respektive bytové posloupnosti. Program mc poskytuje také plnohodnotný textový editor, jenž obsahuje bˇežné funkce jako jiné editory. Za zmínku stojí zvýraznˇení syntaxe nebo podpora maker. Jak prohlížeˇc, tak editor si pˇri ukonˇcení ukládají aktuální pozici v souboru. Na ni se pak pˇresunou pˇri opˇetovném otevˇrení souboru. Jednou z nejužiteˇcnˇejších vlastností mc je zakomponovaná pˇríkazová rˇádka. Standardnˇe ji reprezentuje jednoˇrádkové textové pole, jehož obsah následnˇe zpracuje shell (interpret pˇríkazu). ˚ Toto rˇešení nemusí pokaždé postaˇcovat požadavkum ˚ uživatele a tak se mc na požádání schová a spustí plnohodnotný shell uvnitˇr sebe. Uživatel se pak muže ˚ kdykoliv vrátit zpˇet do mc a nechat v pozadí shell vykonávat pˇríkaz. Tento výˇcet nevyjmenovává úplnˇe všechny schopnosti mc. Vˇetšina z výše zmínˇených je ale pˇredmˇetem této bakaláˇrské práce.
1.2
UTF-8
UTF-8 pˇredstavuje jedno z nejpoužívanˇejších kódování znakové sady Unicode. Pˇred samotným popisem kódování UTF-8 je nutné pˇriblížit nˇekolik pojmu˚ zmínˇených v pˇredchozí vˇetˇe. Znaková sada pˇriˇrazuje vybraným znakum ˚ hodnotu, která je daným zaˇrízením jednodušeji zpracovatelná. Konkrétnˇe pro poˇcítaˇcové zpracování textu˚ jsou znaky mapovány na pˇrirozená cˇ ísla. Naproti tomu kódování pˇredepisuje, jak budou tyto hodnoty uloženy pˇrímo v pamˇeti poˇcítaˇce. První znakové sady používaly nejednodušší možné kódování a ukládaly znak pˇrímo jako cˇ íslo. Dnes se bˇežnˇe pracuje i s komplikovanˇejšími zpusoby ˚ kódování, mezi které se rˇadí i UTF-8. Puvodnˇ ˚ e vzniklo nˇekolik ruzných ˚ znakových sad, z nichž si pouze jedna vydobyla dominantní postavení. A tou se stala puvodem ˚ americká znaková sada ASCII (American Standard Code for Information Interchange) spolu s jejími pozdˇejšími rozšíˇreními. ASCII sada popisuje 128 ruzných ˚ znaku, ˚ takže pro kódování jednoho znaku dostaˇcuje právˇe jeden byte. Jelikož 128 hodnot zabere pouze sedm bitu, ˚ jeden bit z každého bytu zustane ˚ nevyužitý. Prvních 32 znaku˚ a poslední 128. se oznaˇcují jako rˇídící a sloužily puvodnˇ ˚ e k ovládání periferií, jako napˇríklad tiskáren. Jelikož nemají pˇriˇrazenu žádnou grafickou reprezentaci, nazývají se také cˇ asto jako netisknutelné. Dnes se z nich používají jen nˇekteré jako tabulator, line feed, carrige return, backspace nebo escape. První znak s hodnotou nula funguje jako terminátor rˇetˇezcu˚ v programovacím jazyku C (v jazyce C je naprogramován mc). Zbylé znaky již grafickou podobu mají a patˇrí mezi nˇe cˇ íslice, velká a malá písmena anglické abecedy, základní matematické operátory, závorky, interpunkce a jiné speciální znaky, jako napˇr. symbol amerického dolaru. Písmena jsou seˇrazena podle anglické abecedy, velká jsou pˇred malými. 1.
4
nroff formátování pomocí znaku backspace oznaˇcuje, které znaky zvýraznit nebo podtrhnout
1. Ú VOD V bytové reprezentaci se pak jednotlivé dvojice velkých a malých písmen liší pouze v jednom konkrétním bitu, cˇ ehož se využívá pˇri pˇrevodu mezi velkými a malými písmeny. V sadˇe ASCII lze uchovávat pouze texty v angliˇctinˇe, jelikož neobsahuje znaky vyskytující se v abecedách jiných jazyku. ˚ Tento problém se rˇešil jednoduše rozšíˇrením sady ASCII o dalších 128 znaku˚ a tím pádem využitím všech osmi bitu˚ jednoho bytu pˇri ukládání. Vzniklo tak pˇribližnˇe nˇekolik desítek ruzných ˚ znakových sad. Vˇetšinou se lišily znaky, které k ASCII pˇridaly, ale v nˇekterých pˇrípadech dostaly spoleˇcné znaky dvou sad ruzné ˚ pozice v každé z nich. Používaná sada/kódování2 záležela vˇetšinou na operaˇcním systému. Operaˇcní systémy typu UNIX podporovaly kódování ze standardu ISO/IEC 8859, Microsoft pro DOS používal kódování standardizované firmou IBM jako cp437, cp850 nebo cp852 a pro Windows vytvoˇril vlastní znakové sady, v nˇekterých pˇrípadech nekompatibilní s ISO/IEC 8859. Azbuka se ukládala pˇrevážnˇe v samostatných kódováních KOI8-R a KOI8-U navržených sovˇetskými inženýry. Od ISO/IEC 8859-5 urˇceného též pro azbuku se odlišovala nelexikografickým uspoˇrádáním písmen azbuky. Místo toho byly rozmístˇeny tak, aby pˇri odstranˇení posledního bitu každého bytu vznikl pˇrepis azbuky do latinky. Uvedené rˇešení však ani zdaleka nepostaˇcuje pro východoasijské jazyky jako cˇ ínština, japonština nebo korejština. Pro tyto jazyky je už nutné zvolit vícebytové kódování, jelikož jejich abecedy tvoˇrí tisíce a více znaku. ˚ Vˇetšina kódování pˇrijala princip escape sekvence, který byl navržen již pro sadu ASCII k potlaˇcení puvodního ˚ významu znaku. K výchozímu jednobytovému kódování se pˇridaly sekvence o jednom až tˇrech bytech reprezentující znaky z nˇekterých národních znakových sad. Naneštˇestí escape sekvence pˇrinášejí nˇekolik nepˇekných vlastností. Napˇríklad uprostˇred textu nelze snadno rozhodnout, zda se byte nachází v escape sekvenci nebo ne. Jiný pˇrístup pˇredstavuje kódování Big5. To ukládalo znak pokaždé do dvou bytu, ˚ ale jen v omezeném rozsahu. Obsadila se jen necelá horní polovina každého bytu, spodní poloˇ vina bytu zustala ˚ volná. Casto se tak doplnovalo ˇ nˇekterým z jednobytových kódování jako ASCII nebo cp473. Poskytovalo také velkou výhodu pˇri práci v textovém terminálu, nebot’ všechny jeho znaky zabírají dvˇe pole v terminálu. To velice usnadnilo jeho podporu v již používaných terminálových aplikacích, nebot’ správné zobrazení nepotˇrebovalo žádné úpravy pro poˇcítání šíˇrky rˇetˇezcu. ˚ Žádné z pˇredchozích kódování nedovolovalo pˇrímo spojit v jednom dokumentu cˇ ásti v ruzných ˚ jazycích s odlišnými abecedami. Takové pˇrípady se rˇešily až na úrovni znaˇckování dokumentu. A tak na pˇrelomu osmdesátých a devadesátých let minulého století zapocˇ ala pˇríprava standardu ISO/IEC 10646, nebo-li znakové sady, jenž pokryje všechny bˇežnˇe používané znaky, pˇrípadnˇe nˇekteré historické cˇ i speciální. Do standardu bylo zahrnuto i nˇekolik metod kódování novˇe vznikající znakové sady. V roce 1991 se pˇripojilo ke standardizaci konzorcium Unicode, jenž spravovalo standard Unicode. Znaková sada standardu Unicode vycházela ze sady ISO/IEC 10646 a tudíž s ní byla kompatibilní. První verze ISO/IEC 10646 byla omezena na 65 363 (216 ) možných znaku, ˚ následující 31 revize posunula tuto hranici pˇres jednu miliardu (2 ), pozdˇeji však byla snížena na 221 . Doopravdy pˇridˇelených je dnes kolem 100 000, prvních 256 znaku˚ se shoduje se sadou ISO/IEC 8859-1. Oproti stávajícím znakovým sadám je každý znak doplnˇen o základní slovní popis. Mimo klasických písmen obsahuje ruzné ˚ matematické operátory, technické znaˇcky, symboly pro zápis hudebních not nebo Braillovo písmo pro slepé. Vedle samostatných zavádí i speciální kombinaˇcní znaky, které spojením s jinými vytváˇrí nové znaky. Typickým pˇríkla2.
u tˇechto sad se pojmy sada a kódování témˇerˇ nerozlišovaly
5
1. Ú VOD dem jsou všechna diakritická znaménka cˇ i nˇekteré matematické operátory. Zapisují se až za znak, se kterým se spojují. Napˇríklad znak é (U+00E9) se ukládá také jako e (U+0065) a znak cˇ árky (U+0301). To znamená, že všechna existující písmena s diakritikou lze zapsat dvˇema zpusoby ˚ a navíc je možné tvoˇrit i nové kombinace. Tento pˇrístup taktéž zjednodušuje porovnávání textu, ˚ kdy se nejprve posuzuje podle základního znaku a pak pˇrípadných znamének. Pˇredtím však musí být každý text normalizován, aby každý znak s diakritikou a podobné byly zapsány pouze kombinaˇcními znaky. Naopak složená forma reprezentace poskytuje lepší kompatibilitu pˇri pˇrevodu do starších kódování. Standard Unicode se nezabývá jen znakovou sadou a kódováním jako ISO/IEC 10646. Urˇcuje navíc i pravidla pro porovnávaní rˇetˇezcu˚ a jejich normalizaci, postup pˇrí vykreslování zleva doprava i zprava doleva. To vyžaduje doplnit ke všem znakum ˚ další informace, jako napˇríklad výchozí smˇer zobrazení. Ne všechny aplikace dnes implementují úplnou podporu pro standard Unicode, ale podpora znakové sady ISO/IEC 10646 zaˇcíná být samozˇrejmostí. Pro sadu Unicode vzniklo nˇekolik ruzných ˚ kódování. UCS-2 pˇredstavuje dvoubytové, jež jako jediné nepokryje celý souˇcasný Unicode. Prakticky se dnes už nepoužívá a bylo nahrazeno kódováním UTF-16, které kombinuje dvoubytové i cˇ tyˇrbytové sekvence. Nejvˇetší uplatnˇení nachází v nových prostˇredí jako Java nebo .Net, operaˇcní systém Microsoft Windows jím od verze 2000 nahradil UCS-2 pro Unicode rˇetˇezce. Jediné cˇ tyˇrbytové kódování dostalo hned dva názvy a to UCS-4 nebo UTF-32, i když UTF-32 mˇelo být puvodnˇ ˚ e odlišné. Kvuli ˚ vysokým nárokum ˚ na pamˇet’ pˇri ukládání bˇežných znaku˚ se uplatní spíše ve speciálních pˇrípadech. Spoleˇcným problémem pˇredchozích kódování je nekompatibilita s vˇetšinou programu˚ orientovaných na cˇ istˇe jednobytová kódování, pˇredevším ASCII. Nejhuˇ ˚ re se projevuje cˇ astý výskyt nulových bytu, ˚ jenž plní standardnˇe funkci terminátoru rˇetˇezce. Tento neduh odstranuje ˇ až kódování UTF-8. Jako jediné vychází z ASCII a rozšiˇruje jej o sekvence o dvou až šesti bytech uložené v horních polovinách hodnot bytu. ˚ UTF-8 rˇetˇezce mohou být pˇredány standardním funkcím, ale ne všechny budou už z principu pracovat správnˇe. Mezi další pˇríjemné vlastnosti patˇrí schopnost detekce a ignorování chyb. Kdekoliv v textu lze snadno urˇcit zaˇcátek následujícího nebo pˇredchozího znaku. Žádná sekvence není prefixem jiné a tím pádem se nijak nekomplikuje vyhledávání textu. Pˇri uchovávání textu v latince dosahuje minimálního zvýšení pamˇet’ové nároˇcnosti oproti jednobytovým kódováním. Jediná opravdová nepˇríjemnost spoˇcívá ve výskytu neplatných sekvencí, s nimiž se musí všechny funkce vypoˇrádat.
6
Kapitola 2
Pˇrehled situace Aktuální verze mc pracuje dobˇre pouze v prostˇredích s jednobytovými kódováními latinky a azbuky. V ostatních pˇrípadech vzniká vˇetšinou rˇada nepˇresností pˇri zobrazení nebo se objeví potíže se zmˇenou velikosti písmen. Zdrojový kód mc pˇredpokládá, že každý byte rˇetˇezce odpovídá jednomu znaku a zabírá jedno pole na terminálu a že význam všech znaku˚ ze sady ASCII se nikdy nezmˇení. Vubec ˚ nepoˇcítá s vícebytovými sekvencemi a prochází texty výhradnˇe po bytech. Vˇetšinu základních operací s texty vykonává pˇres standardní systémové funkce, u kterých podpora UTF-8 kvuli ˚ jednoduchému rozhraní a zachování zpˇetné kompatibility ani nepˇrichází v úvahu. Vnitˇrní prohlížeˇc umí konvertovat text mezi nejpoužívanˇejšími kódováními pro západní, stˇrední a východní Evropu. Uživatel jen nastaví kódování terminálu a textového souboru a prohlížeˇc si vytvoˇrí vhodnou pˇrevodní tabulku s 256 prvky. Pˇresný obsah tabulky sestaví z výsledku˚ volání funkcí z knihovny iconv.h. Pokud se znak ze zdrojové sady nevyskytuje v cílové, nahradí se teˇckou. Pˇri zobrazení souboru se pak pˇripravenou tabulkou mapují znaky mezi kódováním souboru a terminálu.
2.1
Zobrazení
Aplikace mc vykresluje své rozhraní hned dvˇema knihovnami. Výchozí je knihovna S-Lang [3], kterou pˇrípadnˇe zastoupí knihovna Ncurses [1]. Obˇe dvˇe si dnes bez potíží poradí s UTF8, i když rozdílným zpusobem. ˚ Knihovna S-Lang se musí explicitnˇe pˇrepnout do Unicode režimu voláním funkce Slutf8_enable. Podle parametru bud’ UTF-8 povolí nebo zakáže, pˇrípadnˇe se rozhodne podle kódování prostˇredí. V Ncurses se žádná podobná funkce nevyskytuje a o podpoˇre UTF-8 se rozhoduje už pˇri pˇrekladu knihovny. Z toho duvodu ˚ se v operaˇcních systémech cˇ asto nachází ve dvou verzích jako standardní Ncurses a pak Ncursesw se zakompilovaným rozšíˇrením pro Unicode. Tedy nic nebrání tomu, aby se zachovaly obˇe knihovny pro zobrazení, jelikož rozdíly v implementaci UTF-8 nejsou nijak podstatné. Pˇri výpisu znaku˚ ze sady Unicode na terminálu dochází bohužel k nˇekolika komplikacím. Délka v bytech ani poˇcet znaku˚ rˇetˇezce vˇetšinou neodpovídá poˇctu polí zabraných na terminálu. Jedinou výjimku tvoˇrí texty složené výhradnˇe ze znaku˚ ASCII. Nejvíce komplikací zpusobují ˚ východoasijské (tzv. široké) symboly, jež zabírají prostor dvou polí namísto obvyklého jednoho. U nich hraje duležitou ˚ roli i pˇresné umístˇení. Nˇekdy totiž zustane ˚ poslední volné pole, ale široký znak se do nˇej už nevejde a pˇretekl by mimo vyhrazenou oblast. Tedy pˇred vykreslením se musí zjistit, jestli zbývající místo postaˇcuje, a až pak znak pˇrípadnˇe zobrazit. Program mc ale bˇežnˇe kontroluje jen to, zda se vyhrazený prostor ještˇe nezaplnil. Naproti tomu šíˇrku textu nijak neovlivní kombinaˇcní znaky, jenž se spojí s pˇredchozím normálním znakem. Pˇri jejich zobrazení však nesmí dojít k oddˇelení od znaku, ke kterému se pojí. 7
ˇ 2. P REHLED
SITUACE
2.2
ˇ ezce v UTF-8 Retˇ
2.2.1
Velikost znaku
Nˇekteré funkce mc obˇcas pˇrevádˇejí text do malých nebo velkých písmen, což se v podání UTF-8 také zkomplikuje. Nelze pˇredpokládat, že výsledný rˇetˇezec bude stejnˇe veliký jako puvodní ˚ nebo zustane ˚ zachován asponˇ poˇcet znaku. ˚ Jako pˇríklad se typicky uvádí nˇemecký malý znak ß, jenž se v Unicode pˇrepisuje na SS. To vyluˇcuje použití funkcí jako tolower nebo toupper mimo znaky ASCII. Unicode dokonce nerozlišuje jenom kategorie velká a malá písmena ale i ještˇe tˇretí titulkovou (titlecase) velikost pro složené znaky jako dz nebo lj. V titulkové velikosti se píše první ze složených znaku˚ velkým, zbytek pak malým. 2.2.2
Porovnávání
Další úskalí UTF-8 spoˇcívá v porovnávání textu. ˚ Bˇežnˇe používaná funkce strcmp se hodí pouze v nouzi, kdy není dostupná žádná lepší funkce. Není schopná správnˇe posoudit ani lexikální shodnost rˇetˇezcu˚ natož urˇcit jejich správné poˇradí. Pˇríˇcina spoˇcívá v tom, že Unicode cˇ ást znaku˚ zapisuje více zpusoby, ˚ jako napˇríklad znaky s diakritikou. Nˇekteré znaky se dokonce i opakují z duvodu ˚ zpˇetné kompatibility. Pˇred každým porovnáním se musí rˇetˇezce normalizovat, takže napˇríklad veškerá diakritika se zapíše výhradnˇe kombinaˇcními znaky a nezustane ˚ žádný složený znak. Normalizovanou podobu zvládne úspˇešnˇe porovnávat už i funkce strcmp, ale doporuˇcuje se zvolit funkci, která zohlednuje ˇ národní zvyklosti. Funkce mc také srovnávají texty bez zohlednˇení velikosti písmen. Pro latinku se toho v normalizované podobˇe dosáhne pomˇernˇe snadno, nebot’ se týká jen písmen objevujících se v ASCII, ale pro celý Unicode se volí jiné rˇešení. Libovolnˇe pˇred nebo až po normalizaci se pˇrevedou všechny znaky do své výchozí velikosti. Tato forma nepˇredstavuje bud’ jen malá nebo velká písmena, ale všechny výskyty jednoho znaku mají stejnou velikost. Další postup pˇri porovnání je již stejný. Skoro nereálné se mi však jeví srovnání textu˚ s omezenou délkou jako funkce strncmp. Problém spatˇruji v obtížné definici, ke kterému ze dvou rˇetˇezcu˚ se vlastnˇe zadaná délka váže a co pˇresnˇe oznaˇcuje, jestli vyjadˇruje délku rˇetˇezce cˇ i poˇcet znaku, ˚ a to v puvodní ˚ cˇ i normalizované formˇe. Dokáži si však pˇredstavit rozšíˇrenou variantu, u níž se bude zadávat délka obou rˇetˇezcu, ˚ respektive poˇcet znaku. ˚ 2.2.3
Vyhledávání
Obdobné komplikace doprovází i vyhledávání podˇretˇezce v textu funkcemi strstr (od zaˇcátku) nebo strrstr (od konce). Jako první krok se tedy opˇet vykoná normalizace a pˇrípadnˇe úprava velikosti písmen. Situace se liší jen v tom, že se posuzuje pouze shoda, rozhodovat o uspoˇrádání není nutné. Proto obˇe funkce strstr a strrstr pro vyhledávání v normalizovaném textu již postaˇcují. Obˇe funkce vrací pozici v textu, kde se hledaný podˇretˇezec nachází. Takže výsledek hledání v normalizované formˇe není použitelný a je nezbytné pˇrevést jej tak aby ukazoval na správnou pozici v puvodním ˚ textu. V ideálním pˇrípadˇe vyhledávací funkce vrátí i konec podˇretˇezce v textu, nebot’ i ten se obtížnˇe urˇcuje jen ze znalosti umístnˇení a velikosti podˇretˇezce. Pokud konec nevrací, urˇcí se následovným postupem. Pˇredpokládá se, že normalizace se dotkla pouze kombinaˇcních znaku, ˚ ostatní složené znaky zustaly ˚ nezmˇenˇeny. Spoˇcítá se tedy poˇcet samostatných znaku˚ hledaného podˇretˇezce. Stejný poˇcet samostatných znaku˚ se pak odpoˇcítá od pozice podˇretˇezce v prohledávaném 8
ˇ 2. P REHLED
SITUACE
textu a získá se tak odkaz na první znak, který do nalezeného podˇretˇezce již nepatˇrí. Volitelnˇe se výsledek zkontroluje tak, že se porovná délka podˇretˇezce i jeho výskytu v textu v normalizovaném tvaru. Když se shodují, je vše v poˇrádku. Pokud ne, posune se pozice konce o rozdíl obou délek vhodným smˇerem a kontrola se zopakuje.
2.3
Puvodní ˚ UTF-8 patch
Pro mc už jeden pokus o implementaci UTF-8 existuje. Takto upravená verze mc se cˇ asto distribuuje v operaˇcních systémech, které volí jako výchozí kódování UTF-8. Vžilo se pro nˇej oznaˇcení UTF-8 patch. Soustˇred’uje se pˇrevážnˇe na korekce zobrazení a zpracování vstupu z klávesnice. Úpravy jim provedené místy pˇripomínají spíše rychlé opravy chyb (viz pˇríklady 2.1 a 2.2) než nˇejaké ucelené rˇešení problému˚ s Unicode. Manipuluje s Unicode rˇetˇezci prostˇrednictvím funkcí z knihoven wchar.h a wctype.h. Obˇe knihovny spoleˇcnˇe pˇredstavují standard pro operace s texty ve vícebytovém kódování. Jejich chování se pˇrizpusobuje ˚ nastavení prostˇredí. Naneštˇestí se v nich nenacházejí prvky typické pro Unicode, jako napˇríklad kombinaˇcní znaky. Unicode prostˇredí detekuje funkcí SLsmg_is_utf8_mode z knihovny S-Lang. Tím cˇ iní mc na této knihovnˇe nevyhnutelnˇe závislým, i když puvodnˇ ˚ e mc v pˇrípadˇe potˇreby chybˇející S-Lang nahradil knihovnou Ncurses. Nijak se nezabývá podporou externích souborových systému˚ v odlišných kódování, než používá mc. Pˇri kopírování a pˇresunu souboru˚ nahrazuje z nouze neplatné UTF-8 sekvence otazníky a niˇcí tak originální názvy souboru. ˚ Zachování puvodní ˚ bytové podoby názvu souboru se mi jeví jako praktiˇctˇejší rˇešení, pokud to souborový systém podporuje. Integrovaný prohlížeˇc nevykresluje správnˇe široké a kombinaˇcní znaky a obecnˇe mu vícebytové sekvence cˇ inní potíže. Nelze jej však tak malými úpravami dokonale pˇrizpusobit ˚ a je tˇreba jej kompletnˇe pˇrepsat. Na druhou stranu editor se mi zdá již pomˇernˇe použitelný. Avšak ani jeden z nich nedovoluje zadat libovolné kódování pˇri otevírání souboru.
9
ˇ 2. P REHLED
SITUACE
// Nalezne rychlou klávesu v textu tlaˇ cítka a uloží ji // do hotkey. Do hotpos vloží sloupec horké klávesy v textu. // Zruší znak & v textu tlaˇ cítka static void button_scan_hotkey (WButton *b) { char *cp = strchr (b->text, ’&’); if (cp != NULL && cp[1] != ’\0’) { g_strlcpy (cp, cp + 1, strlen (cp)); b->hotkey = tolower ((unsigned char) *cp); b->hotpos = cp - b->text; } } Spoleˇcnˇe s pˇríkladem 2.2 ukazuje rozdíl mezi puvodní ˚ a upravenou verzí detekce horké klávesy. V tomto pˇrípadˇe se netypicky zmˇenila i deklarace funkce, nebot’ ji lze využít i pro jiné ovládací prvky než jen tlaˇcítko. Pˇríklad 2.1: Puvodní ˚ detekce rychlé klávesy
10
ˇ 2. P REHLED
SITUACE
static void scan_hotkey (char *text, int *hotposp, int *hotkeyp, wchar_t *hotwcp) { char *cp = strchr (text, ’&’); if (cp != NULL && cp[1] != ’\0’) { #ifdef UTF8 if (SLsmg_Is_Unicode) { mbstate_t s; int len; *cp = ’\0’; memset (&s, 0, sizeof (s)); len = mbrtowc (hotwcp, cp + 1, MB_CUR_MAX, &s); if (len > 0) { *hotposp = mbstrlen (text); if (*hotposp < 0) { *hotposp = -1; } else { /* FIXME */ *hotkeyp = tolower (*hotwcp); } } } else #endif { *hotkeyp = tolower (cp[1]); *hotposp = cp - text; } memmove (cp, cp + 1, strlen (cp + 1) + 1); } } Pˇríklad 2.2: Úprava detekce rychlé klávesy (UTF-8 patch)
11
Kapitola 3
Úpravy pro UTF-8 Rozhodl jsem se puvodní ˚ UTF-8 patch celý pˇrepracovat a vytvoˇrit v mc jednotný systém pro manipulaci s rˇetˇezci. UTF-8 patch mi tak posloužil jako základní pˇrehled problému˚ s UTF-8, které bylo potˇreba v mc rˇešit, ale jen málo jsem z nˇej zachoval.
3.1
Výbˇer základní metody
Na zaˇcátku samotné práce jsem si musel zvolit, jak pˇresnˇe se s UTF-8 vypoˇrádám. Zvažoval jsem dva pˇrístupy. Jeden se pokusí skrýt rozdíly mezi UTF-8 a jednobytovými kódováními zavedením nového rozhraní pro rˇetˇezce. Druhý pak prosazuje UTF-8 jako jediné kódování i v jiných než Unicode prostˇredích. Oba pˇrístupy mají samozˇrejmˇe své výhody i nevýhody. První zpusob ˚ implicitnˇe pˇredpokládá definici rutin rozhraní pro rˇetˇezce. To znamená pˇripravit seznam vyžadovaných funkcí a implementovat je pro každé podporované kódování. Úspˇech mé práce pak bude záležet na kvalitˇe návrhu tohoto rozhraní. A také jestli se jeho podoba nebude cˇ asto mˇenit a poˇcet funkcí se ustálí na rozumné hranici nˇekde mezi dvaceti až tˇriceti. Výraznˇe vˇetší poˇcet by znamenal složitˇejší údržbu v budoucnosti a nepˇrispˇel by nijak k pˇrehlednosti mezi jednotlivými operacemi. Hlavní výhoda prvního rˇešení spoˇcívá v zachování pˇribližnˇe stejné procesorové a pamˇet’ové nároˇcnosti pro starší znakové sady jako v souˇcasném mc. V krajním pˇrípadˇe, kdy nebude dostupná žádná knihovna pro Unicode, se podpora pro UTF-8 zcela vypustí, ale schopnosti mc tím nebudou nijak zásadnˇe ohroženy. Naopak udržování nˇekolika variant od každé funkce si vyžádá nˇejakou práci navíc, obzvlášt’ když se zmˇení zásadnˇe význam nˇekteré z nich. Druhá metoda pˇristupuje k Unicode více progresivnˇe a volí UTF-8 jako jediné kódování. Kompatibilita s jinými než Unicode prostˇredími se zajistí pˇrevodem všech vstupu˚ do UTF-8 a výstupu˚ nazpˇet z UTF-8. Vypoˇrádá se tedy s jakýmkoliv kódováním pouze jednou sadou funkcí, ale za cenu vyšší nároˇcnosti. Také dokáže uvnitˇr mc reprezentovat libovolný Unicode znak, i když jej nelze na terminálu zobrazit. V systémech bez podpory Unicode se však nepodaˇrí ani kompilace mc bez zásadnˇejších úprav. Další potíže zpusobují ˚ funkce pro lokalizaci jako gettext. Vracejí totiž rˇetˇezce výhradnˇe v kódování prostˇredí a ty se pak musí pˇrevést do UTF-8. Obdobnˇe se chovají i standardní funkce pro vyhodnocování regulárních výrazu, ˚ takže mimo Unicode prostˇredí jsou nepoužitelné. Obˇe varianty spojuje jeden spoleˇcný prvek. Zavádˇejí rozhraní pro práci s texty, jenž v obou pˇrípadech bude z vˇetší cˇ ásti shodné. Hlavnˇe se však nijak vzájemnˇe nevyluˇcují. Rozhodl jsem se tedy obˇe rˇešení zkombinovat. Nejbˇežnˇejší kódování jako ASCII, jednobytová a UTF-8 dostanou vlastní implementaci všech funkcí. Zbývající se pak vyˇreší konverzí do UTF-8 a nazpˇet. Zkombinují se tak pˇrednosti obou dvou variant. Jako první krok ještˇe pˇred samotným programováním jsem musel zvolit knihovnu pro práci s Unicode rˇetˇezci. Napsat si potˇrebné funkce sám totiž není pˇri rozsahu standardu Uni13
3. Ú PRAVY
PRO
UTF-8
code reálné. Vybíral jsem mezi knihovnami wchar.h spolu s wctype.h a GLib [5]. Jedinˇe GLib však nabízí dostateˇcnou podporu pro Unicode (napˇríklad již zmínˇené kombinaˇcní znaky a normalizace rˇetˇezcu). ˚ A jelikož mc již knihovnu GLib používá, padla moje volba na ni. Následnˇe jsem aplikoval na aktuální verzi mc puvodní ˚ UTF-8 patch a zaˇcal jsem jej pˇrepracovávat a doplnovat. ˇ Získal jsem tak výhodu plnˇe funkˇcní aplikace po dobu celého vývoje a mohl jsem tak ihned své zmˇeny dostateˇcnˇe otestovat. Mezitím se ale vývoj mc nezastavil, takže jsem po dokonˇcení práce ještˇe provedl menší modifikace a vše sladil s nejnovˇejší verzí programu.
3.2
Jednotlivé úpravy
Výsledek práce jsem rozdˇelil do více jak tˇriceti samostatných modifikací a to bud’ podle tématu nebo souboru, jenž zmˇenily. Uvádím jen ty, které se vˇenovaly nˇekterým speciálním problémum. ˚ Zbylé pak shrnuji v menší rekapitulaci. 3.2.1
mc-00-slang
Jak už bylo zmínˇeno, u knihovny S-Lang se musí podpora UTF-8 pˇred použitím aktivovat. Kontroluje se též verze knihovny, nebot’ až od verze 2.0 obsahuje podporou Unicode spolu s funkcí SLutf8_enable. 3.2.2
mc-01-api
Už její velikost (tvoˇrí cˇ tvrtinu všech zmˇen) napovídá, že se jedná o stˇežejní cˇ ást mé bakaláˇrské práce. Bez ní zbývající úpravy postrádají smysl. V prubˇ ˚ ehu vývoje zaznamenala nejvíce zmˇen, z kterých stojí za zmínku napˇríklad opoždˇené doplnˇení podpory kombinaˇcních znaku. ˚ Aktuální verze pokrývá plnˇe UTF-8, ASCII a všechna jednobytové kódování, jejichž tisknutelné znaky zabírají na terminálu jedno pole. Jako výchozí sada slouží funkce pro ASCII. Funkce pro ostatní kódování metodou pˇrevodu do UTF-8 a zpˇet jsem ještˇe nepˇripravil. Vyzkoušel jsem si však už, že je tento postup uskuteˇcnitelný. Nenaprogramoval jsem je hlavnˇe proto, že vyˇckávám, až se rozhraní pro rˇetˇezce opravdu stabilizuje. Bylo by totiž zbyteˇcnˇe namáhavé udržovat další pomˇernˇe komplikovanou sadu funkcí. To neplatí pro ASCII a jednobytové rozhraní, jelikož z valné vˇetšiny se volají pouze systémové funkce. Nikdo si také na vynechání tˇechto exotiˇctˇejších kódování zatím nestˇežoval. Jedinou pˇrekážkou pro tato kódování zustává ˚ tak i nadále lokalizace a regulární výrazy, i když mˇe už napadly nˇejaké zpusoby ˚ jejich rˇešení. Napˇríklad jsem zkoušel konvertovat veškerou lokalizaci do UTF-8 za bˇehu. Naneštˇestí si tato metoda vyžádala pˇríliš rozsáhlé zmˇeny, a tak jsem ji zavrhl. Pˇredtím, než se zavolá jakákoliv z nových funkcí, musí se nastavit nˇekteré promˇenné a hlavnˇe vybrat patˇriˇcnou sadu funkcí. To vše provede funkce str_init_strings. Kódování automaticky detekuje funkcí nl_langinfo z knihovny langinfo.h nebo jí jej lze pˇredat pˇrímo jako parametr. Rostoucí rˇetˇezce Jazyk C nijak nedefinuje standardní rˇetˇezce s promˇenou velikostí. Každá aplikaci se s nimi musí vypoˇrádat sama, pokud je potˇrebuje. I v mc se nˇekolika místech vyskytuje jejich im14
3. Ú PRAVY
PRO
UTF-8
plementace, bohužel nikterak univerzální. Navrhl jsem tedy novou strukturu str_buffer (pˇríklad 3.1) a k ní nˇekolik funkcí. Pamatuje si velikost rˇetˇezce, kde se nachází poslední ˇ ezec získá program voláplatný znak a kolik za ním zbývá ještˇe místa v pˇridˇelené pamˇeti. Retˇ ním funkce str_get_buffer a až jej nepotˇrebuje, vrátí jej funkcí str_release_buffer nazpˇet. Ve skuteˇcnosti se však pˇridˇelená pamˇet’ neuvolní, ale použije pro další str_buffer znovu. Operace nad rostoucími rˇetˇezci zahrnují vložení jiného rˇetˇezce cˇ i znaku, jeho vyprázdnˇení nebo variantu funkce printf. Všechny dovedou zvˇetšit kapacitu pˇridˇelené pamˇeti pro rˇetˇezec v pˇrípadˇe potˇreby. Pˇreklad textu Další cˇ ást rozhraní se vˇenuje pˇrevodu kódování. Systémová knihovna iconv.h sice splnuje ˇ požadavky mc, ale její rozhraní je pˇríliš obecné. Nové funkce tedy jen usnadnují ˇ konverze textu˚ v programu. Pˇred pˇrekladem se nejprve definuje zdrojové a cílové kódování. Pˇrevádí se bud’ z kódování rozhraní do jiného nebo obrácenˇe, takže se vybírá jen smˇer a druhé kódování. Vytvoˇrí se tak konvertor, kterým se texty pˇrevádí. Jelikož se dopˇredu špatnˇe odhaduje velikost výsledného pˇrekladu, ukládá se do zadaného rostoucího rˇetˇezce. Až již nadále není konvertor potˇrebný, jednoduše se uvolní. Navíc jsou definovány tˇri speciální konvertory: str_cnv_not_convert, str_cnv_from_term a str_cnv_to_term. První se uplatní v situaci, kdy se zdrojové a cílové kódování shodují a text staˇcí pouze zkopírovat. Další dva jsou pˇripraveny pro pˇrípad rozdílných kódování rozhraní a systému, tedy pro již zmínˇenou cˇ tvrtou dosud neimplementovanou sadu funkcí. Urˇcovací funkce Každý znak pˇrísluší do jedné nebo více skupin. Typicky se rozlišují znaky rˇídící, tisknutelné, alfanumerické a podobné kategorie. Dále se urˇcuje velikost písmen a v UTF-8 i zda neobsahuje neplatné sekvence. V této cˇ ásti se neobjevuje žádná potíž a prakticky se jedná jen o pouhé zabalení funkcí z knihovny GLib nebo systémových volání. Na rozdíl od systémových funkcí místo s jedním bytem pracují se sekvencemi bytu. ˚ Iteraˇcní funkce Procházení rˇetˇezcu˚ znak po znaku pˇredstavuje nejˇcastˇeji s nimi provádˇenou cˇ innost. Jelikož se UTF-8 rˇadí mezi kódování s promˇennou délkou, pˇrechod na následující nebo pˇredchozí znak není jednoduchá operace. Zvlášt’ když rˇetˇezec obsahuje i neplatné sekvence. Kvuli ˚ nim také knihovna GLib poskytuje hned dvˇe varianty funkcí, jednu rychlejší, ale bez kontroly a druhou pomalejší s detekcí chyb v rˇetˇezci. Toto rozdˇelení jsem pˇrejal a doplnil ještˇe tˇretí variantu, jež pˇreskakuje kombinaˇcní znaky. Využití nalezne pˇri editaci textu, protože uživatel neoˇcekává pohyb kurzoru po jednotlivých kombinaˇcních znacích. Poˇcet znaku˚ rˇetˇezce Bohužel funkce z knihovny GLib k tomu urˇcená nepracuje s neplatnými UTF-8 rˇetˇezci, takže jsem byl donucen poˇcítat znaky vlastní funkcí. Každý byte neplatné sekvence se považuje za samostatný znak. I zde existuje varianta, jenž nezapoˇcítává kombinaˇcní znaky. 15
3. Ú PRAVY
PRO
UTF-8
Formátovací funkce Pˇredtím než mc vykreslí jakýkoliv znak nebo rˇetˇezec na konzoli, zkontroluje jej a nahradí všechny netisknutelné znaky teˇckou. Spoleˇcnˇe s tím se text zarovná a pˇrípadnˇe jej zkrátí nebo prodlouží mezerami tak, aby plnˇe pokryl vyhrazený prostor na terminálu. Pro zarovnání neexistují v mc žádné pomocné funkce a poˇcítá se pˇrímo na místˇe, kde je vyžadováno. Naopak zkracování rˇetˇezcu, ˚ pˇrevážnˇe jmen souboru, ˚ zajišt’ují dvˇe funkce. Jedna vypustí prostˇrední cˇ ást a vloží místo ní znak ˜, druhá ponechá jen konec jména a zaˇcátek nahradí tˇremi teˇckami. Navrhl jsem tedy nˇekolik funkcí, které upraví text pˇred zobrazením na terminálu. Vˇenují se jak zkracování tak zarovnání textu. ˚ Obvyklé volby jako zarovnat doleva, doprava nebo na stˇred jsem rozšíˇril o novou spojující dvˇe puvodní ˚ dohromady. Pokud text nepˇresahuje vyhrazený prostor, zarovná jej na stˇred. Pokud ne, zaˇrízne text od konce. UTF-8 varianta formátovacích funkcí se zabývá též nˇekterými specifickými problémy, proto jí vˇenují ještˇe trochu pozornosti. První spoˇcívá v implementaci. Spousta cˇ inností pˇri formátování se totiž opakuje nˇekolikrát i v jedné funkci. V pˇrípadˇe UTF-8 však jedna taková cˇ innost zabere až deset rˇádku˚ a cˇ inní tak kód znaˇcnˇe nepˇrehledným. Zpravidla ani nelze snadno urˇcit, co pˇresnˇe provádí. Povedlo se mi je všechny naštˇestí zobecnit do nˇekolika pomocných rutin, jako napˇríklad vlož urˇcitý poˇcet mezer do výsledku nebo vynechej nˇekolik znaku. ˚ Tato zmˇena pak podstatnˇe zjednodušila doplnˇení podpory pro kombinaˇcní znaky. A právˇe ony pˇredstavují další specifickou vlastnost UTF-8. Témˇerˇ vždy mají šíˇrku nula s jedinou výjimkou. A to když text nˇejakým zaˇcíná. V tomto pˇrípadˇe se vloží pˇred nˇej mezera, aby nenarušil zobrazení jiných prvku. ˚ Ve výsledku se tak zabere jedno pole. Nˇekteré terminály nezobrazují kombinaˇcní znaky dobˇre a proto se pˇred vykreslením pˇrevedou na složené znaky. Nakonec se ještˇe opraví všechny neplatné sekvence speciálním Unicode znakem k tomu urˇceným. Porovnávání a vyhledávání Pro klasická jednobytová kódování existují systémové funkce jako strcmp pro porovnání nebo strstr pro vyhledání. Jejich obdoba pro UTF-8 se opˇet nachází v knihovnˇe GLib. Bohužel pˇred každou operací se rˇetˇezce normalizují, což ústí ve zvýšenou procesorovou nároˇcnost a cˇ asté pˇridˇelování pamˇeti. Naštˇestí GLib umožnuje ˇ normalizovat texty pˇredem. Pro srovnání normalizovaných tvaru˚ pak staˇcí i funkce strcmp. Na stejném principu stojí i vyhledávání v textu, u nˇej se však opakuje pouze hledaný rˇetˇezec. Klasické porovnání s omezenou délkou jsem do rozhraní nezahrnul. Místo nˇej slouží operace, jež oznaˇcí za shodné rˇetˇezce, když jeden z nich tvoˇrí prefix druhého. Tím se kompenzuje nejˇcastˇejší použití délkou omezeného srovnání. Nevýhoda funkcí z GLib se ale projeví v pˇrípadˇe, kdy rˇetˇezce obsahuje neplatné sekvence. S nimi si totiž normalizace neporadí. Proto normalizací projdou jen platné sekvence a následnˇe se zkombinují s neplatnými opˇet dohromady. Ve výsledku se tedy stále vyskytují chybné sekvence, ale funkcím jako strcmp to již nevadí. 3.2.3
mc-02-hotkey
Horké klávesy urychlují volbu konkrétního prvku uživatelského rozhraní. Jednotlivým prvkum ˚ se pˇriˇradí horká klávesa na základˇe textu, jenž zobrazují. Pokud se v nˇem vyskytne 16
3. Ú PRAVY
PRO
UTF-8
znak &, následující znak pˇredstavuje horkou klávesu. Ta se vˇetšinou pˇri vykreslení barevnˇe odliší, naopak znak & se úplnˇe vynechá. Po stisknutí horké klávesy se pak aktivuje pˇríslušný prvek. Mechanizmus horkých kláves v mc spoléhá na typické vlastnosti jednobytových kódování a pro UTF-8 je zcela nevyhovující. Dokonce se v mc opakuje nˇekolik podobných rutin pro detekci horké klávesy. Proto jsem zavedl novou strukturu hotkey_t (pˇríklad 3.2) pro text s horkou klávesou spoleˇcnˇe s nˇekolika novými operacemi. Teoreticky tak horká klávesa muže ˚ být jakýkoliv Unicode znak, ale obsluha zpráv v mc neumožnuje ˇ vícebytové horké klávesy, tedy platí omezení pouze na ASCII znaky. Horké klávesy mimo znakovou sadu ASCII neshledávám ani žádoucími, kvuli ˚ jejich závislosti na konkrétní variantˇe rozložení znaku˚ na klávesnici. 3.2.4
mc-03-button
Touto úpravou zaˇcínají konkrétní zmˇeny v kódu mc. První zmˇeny se vˇenují ovládacím prvkum ˚ a právˇe modifikace tlaˇcítka demonstruje témˇerˇ všechny vyskytující se problémy a jejich rˇešení. Nejprve jsem nahradil jednoduchý rˇetˇezec reprezentující text tlaˇcítka strukturou hotkey_t. S tím souvisí i oprava poˇcítaní šíˇrky tlaˇcítka. Puvodní ˚ funkci strlen tak vystˇrídala funkce hotkey_width. Potom jsem pˇrepracoval vykreslení tlaˇcítka. V originální verzi se nejprve zobrazí celý text a až poté se barevnˇe zvýrazní znak horké klávesy. Musí se tedy urˇcit pˇresná pozice znaku horké klávesy na terminálu. V upravené verzi se postupnˇe vykreslí všechny tˇri cˇ ásti struktury hotkey_t, horká klávesa se tak ihned barevnˇe odliší. U ostatních ovládacích prvku˚ jsem postupoval obdobnˇe a proto zmíním už jen pˇrípady, jenž z popsaného postupu výraznˇeji vyboˇcují. 3.2.5
mc-06-input
Prvek jednoˇrádkové textové pole se od ostatních výraznˇe odlišuje. Jako jediný zpracovává a uchovává vstup z klávesnice a jeho šíˇrka se nikdy nemˇení v závislosti na editovaném textu. A první komplikace nastávají právˇe již pˇri cˇ tení vstupu z klávesnice. Obsluha událostí v mc totiž posílá UTF-8 sekvence po bytech a ne celé. Textové pole si pak jednotlivé byty ukládá a až naˇcte celou a platnou sekvenci pˇridá ji do textu. Zvažoval jsem i principiálnˇe lepší úpravu obsluhy zpráv v mc tak, aby prvky pˇrijímaly pˇrímo celé sekvence. Obával jsem se však pˇríliš velkého rozsahu zmˇen, jenž by mohli zbyteˇcnˇe zabránit pˇrijetí výsledku vývojáˇri mc. Zvlášt’ když první metoda pracuje bezproblémovˇe. Dále jsem se rozhodoval, zda pˇri editaci ponechat neplatné sekvence nebo je nahradit otazníky. Jelikož se v textovém poli cˇ asto editují jména a cesty k souborum, ˚ zvolil jsem radˇeji zachování všech puvodních ˚ bytu, ˚ abych omezil zbyteˇcnou ztrátu informací. A to i za cenu mírnˇe zvýšené komplikovanosti programu. 3.2.6
mc-10-buttonbar
Ovládací prvek pruh tlaˇcítek, který se zobrazuje na posledním rˇádku terminálu, je zˇrejmˇe jediné místo, kde jsem se nedržel pˇrísnˇe vzoru. Puvodní ˚ verze totiž nevyužívá celé šíˇrky 1 terminálu a jednotlivá tlaˇcítka mívají rozdílnou velikost , což pusobí ˚ lehce chaoticky. Vˇetšina popisku˚ se v cˇ eštinˇe ani na tlaˇcítka nevejde. Dovolil jsem tedy zvˇetšit všechna tlaˇcítka tak, 1.
platí minimálnˇe pro cˇ eskou lokalizaci, v anglické jsou v poˇrádku
17
3. Ú PRAVY
PRO
UTF-8
aby dohromady pokrývala co nejvíce z šíˇrky rˇádku a pˇritom velikost všech tlaˇcítek zustala ˚ stejná. 3.2.7
mc-11-panel
Panely pˇredstavují nejduležitˇ ˚ ejší cˇ ást rozhraní mc, jeho klíˇcovou vlastnost. Zobrazení panelu se doˇckalo podobných zmˇen jako ostatní prvky. Navíc došlo k výraznému zjednodušení kódu, nebot’ výpoˇcet zarovnání rˇetˇezcu˚ byl puvodnˇ ˚ e jeho souˇcástí. Dále jsem mírnˇe upravil pomocné funkce pro formátování data a cˇ asu, jelikož své výsledky ukládaly pro UTF-8 do pˇríliš malého prostoru v pamˇeti. Jedinou komplikaci zpusobilo ˚ hledání v panelu podle zaˇcátku jména souboru. Podobnˇe jako u textového pole i zde se ze vstupu z klávesnice nejdˇríve poskládá platná UTF-8 sekvence a až pak se zaˇradí na konec hledaného prefixu. Následnˇe se zkontroluje, jestli panel obsahuje soubor, jehož jméno zaˇcíná zadaným rˇetˇezcem. Pokud ano, pˇresune se na nˇej aktuální pozice v panelu. Pokud ne, poslední pˇridaný znak se opˇet smaže. Bohužel tento postup pracuje správnˇe pouze, když se omezí na cˇ istˇe bytovou rovnost. Proto jsem zavedl podobnou metodu s využitím funkce str_prefix. Ta vrací, v kolika znacích se shodují oba zadané rˇetˇezce po pˇrevedení do normalizovaného tvaru. Když se jí jako oba parametry pˇredá stejný rˇetˇezec, výsledkem je poˇcet znaku˚ rˇetˇezce v normalizované formˇe. Novˇe se pak po pˇridání nového znaku zjistí, zda se v panelu nenachází jméno souboru, jenž se plnˇe shoduje s hledaným prefixem. Pokud se najde, už se dál nic nepoˇcítá a aktuální pozice se pˇresune na nˇej. V opaˇcném pˇrípadˇe se vybere jméno souboru, jenž dosáhlo nejvyšší shody s hledaným prefixem a prefix se zkrátí tak aby nepˇresahoval pˇres vybrané jméno. 3.2.8
mc-13-file
Pˇresun, kopírování a mazání souboru˚ patˇrí mezi základní operace každého správce souboru. ˚ V mc vše zajišt’uje systém nˇekolika podobných dialogu. ˚ Jméno zdrojového souboru se testuje zda vyhovuje zadanému regulárnímu výrazu. Nˇekteré regulární výrazy rozdˇelí jméno na více cˇ ásti a z nich uživatel libovolnˇe sestaví jméno cílového souboru. Pˇritom na jeho pˇrání také upraví velikost písmen. Regulární výrazy ovšem nefungují nad rˇetˇezci s neplatnými sekvencemi. Zachoval jsem se stejnˇe jako první UTF-8 patch a všechny neplatné sekvence nahrazoval otazníky. Puvodní ˚ jméno souboru tak nikdy nezustalo ˚ zachováno. Domníval jsem se, že neexistuje zpusob, ˚ jak originální jméno zachovat. Pak jsem si ale všiml, že puvodní ˚ a opravený rˇetˇezec se liší jen v neplatných sekvencích, velikost rˇetˇezce ani ostatní znaky se nezmˇenily. Regulárním výrazem se vyhodnotí tedy opravená varianta, ale jméno cílového souboru se sestaví z originálního i s neplatnými sekvencemi. Toto rˇešení postaˇcuje plnˇe pro jednoduché regulární výrazy. Komplikovanˇejší nedoporuˇcuji zkoušet, nebot’ se pro chybné rˇetˇezce huˇ ˚ re odhaduje správný výsledek. Samozˇrejmˇe na korektní jména souboru lze aplikovat jakýkoliv regulární výraz. 3.2.9
mc-15-dialog
Jak jsem už zmínil, obsluha událostí v mc zpracovává vícebytové sekvence znaku˚ po jednotlivých bytech a nedokáže si je spojit dohromady. Tím pádem není schopná odhalit a zachytit neplatné sekvence a jejich detekci pˇrebírají až ovládací prvky. Uvolnil jsem puvodní ˚ kontroly 18
3. Ú PRAVY
PRO
UTF-8
vstupu z klávesnice, aby se neztratil žádný byte z jakékoliv sekvence. Testovaly a propouštˇely totiž dál jen byty obsahující alfanumerické znaky. Naopak jsem omezil rozsah horkých kláves pouze na znaky ze sady ASCII. 3.2.10 mc-20-dir Následující úprava se vˇenuje tˇrídˇení souboru˚ v panelech. Seˇrazují se nejˇcastˇeji podle jména, velikosti cˇ i cˇ asu poslední modifikace. Právˇe tˇrídˇení podle jména se týkají všechny zmˇeny. Na zaˇcátku se pˇripraví pro všechny soubory porovnávací klíˇce. Potom se soubory uspoˇrádají podle pˇriˇrazených klíˇcu˚ a nakonec se všechny klíˇce uvolní. Knihovna GLib obsahuje speciální funkci pro UTF-8 rˇetˇezce, jenž vytvoˇrí kliˇc vhodný pro lokalizované porovnání jmen souboru. ˚ Neignoruje totiž teˇcku, jak se cˇ asto stává u standardních lokalizovaných porovnání. 3.2.11 mc-26-help, mc-27-hint Jednotlivé lokalizované nápovˇedy a tipy si uchovává mc v souborech s kódováním typickým pro danou lokalizaci, nikde není však pˇrímo uvedeno. To zamezuje jakékoliv rozumné manipulaci s nimi mimo výchozí kódování, tedy i v UTF-8. Proto jsem zkonvertoval všechny soubory nápovˇed a tipu˚ do UTF-8. Naˇctený soubor pak staˇcí pˇrevést do kódování terminálu. Dosáhne se tím tak snadného a správného zobrazení nápovˇedy nebo tipu˚ kdekoliv. 3.2.12 mc-29-vfs Pˇridává do mc novou schopnost pˇri práci se soubory. Nyní lze zmˇenit kódování libovolného adresáˇre a jména všech jeho položek se automaticky pˇreloží pˇri cˇ tení nebo nebo zmˇenˇe obsahu adresáˇre. Jména se ale musí zkonvertovat bez jediného problému do kódování rozhraní pro rˇetˇezce, jinak nepujde ˚ obnovit jejich puvodní ˚ tvar, potˇrebný pro jakoukoliv manipulaci. Položky, jejichž jména tuto podmínku nesplnují, ˇ se v panelu nezobrazí. Naopak pˇri pˇrevodu nazpˇet do kódování adresáˇre se nahradí otazníky všechny nepˇreložitelné znaky. Virtual File System nedovoluje pˇriˇradit nový atribut k adresáˇrum ˚ a tak zbývá jediný zpusob ˚ a to zahrnout zmˇenu do cesty k adresáˇri. Uvodí se rˇetˇezcem #enc: následovaným názvem nového kódování adresáˇre. Jména adresáˇru˚ a souboru˚ nacházející se v cestˇe až za zmˇenou se pak konvertují. Puvodnˇ ˚ e jsem jsem zamýšlel toto rozšíˇrení implementovat jako další tˇrídu VFS podobou jiným, jako napˇríklad tˇrídˇe pro pˇripojení k FTP serverum. ˚ Každá tˇrída poskytuje vlastní souborové a adresáˇrové operace a VFS se pak podle nˇekolika definovaných rˇetˇezcu˚ a jejich výskytu v cestˇe rozhodne, kterou tˇrídu pro daný soubor cˇ i adresáˇr zvolit. Napsal jsem tedy vlastní operace, jež pouze pˇreložily jména souboru˚ a adresáˇru˚ a nový tvar už bez #enc: rˇetˇezce znovu odeslaly ke zpracování. Funkˇcnost byla témˇerˇ dokonalá až na jednu výjimku. Jiné tˇrídy názvy souboru nepˇrekládaly a vznikaly tak symbolické odkazy s cestou nepˇreloženou nazpˇet do systémové tvaru, tedy obsahující #enc: rˇetˇezce, které mimo mc nepodporuje žádná aplikace. Pˇristoupil jsem tedy k druhé variantˇe. Funkce VFS pˇreloží jméno souboru hned na zacˇ átku a až pak výsledek pˇredají operacím pˇríslušné tˇrídy. Pˇri cˇ tení obsahu adresáˇre naopak konvertují jména souboru˚ do kódování rozhraní pro rˇetˇezce. Automatická detekce kódovaní jiného souborového systému se prozatím neprovádí, ale i s ní jsem pˇri návrhu poˇcítal. Pod19
3. Ú PRAVY
PRO
UTF-8
porovaná kódování jsem omezil pouze ASCII kompatibilní, pˇresnˇeji jen na ta uvedená v seznamu supported_encodings. To proto, aby význam základních znaku˚ zustal ˚ nemˇenný a neobjevily se žádné komplikace. Do seznamu jsem zaˇradil nejpoužívanˇejší kódování vycházející z ASCII, jako napˇríklad standard ISO/IEC 8859, KOI8-R i KOI8-U a samozˇrejmˇe UTF-8. Pokud uživatel zmˇení aktuální adresáˇr v mc, zmˇení se i v integrované pˇríkazové rˇádce. Ta by ovšem rˇetˇezec #enc: nevyhodnotila správnˇe. Proto se pˇred zmˇenou adresáˇre pˇríkazové rˇádky pˇrevede aktuální cesta do systémového kódování. Podobnˇe jsem ošetˇril i spouštˇení externích pˇríkazu˚ nad soubory. Nepˇrirozenˇe muže ˚ na uživatele pusobit ˚ chování pˇri pˇresunu podadresáˇre mezi adresáˇri v ruzných ˚ kódování. Jméno podadresáˇre se správnˇe pˇreloží, ale souboru˚ uvnitˇr nˇej se pˇreklad nedotkne. Za oˇcekávanˇejší považuji konverzi jak jména podadresáˇre tak i všech souboru˚ v nˇem. Bohužel kvuli ˚ nebezpeˇcí výskytu chyb typu nedostateˇcných práv k souborum ˚ podadresáˇre nelze zajistit spolehlivé pˇrejmenování všech souboru˚ obsažených v podadresáˇri pˇrímo na úrovni VFS. 3.2.13 mc-30-view Integrovaný prohlížeˇc souboru˚ prodˇelal nejzásadnˇejší úpravy ze všech cˇ ástí mc. Než se k nim však dostanu, vyjmenuji vlastnosti, které jsem chtˇel zachovat nebo pˇridat. Jak už jsem zmínil v úvodu, prohlížeˇc v sobˇe skrývá i hexadecimální editor souboru. ˚ Oba dva módy jsou znaˇcnˇe provázány. Napˇríklad nalezený rˇetˇezec v textu zustane ˚ oznaˇcený i po pˇrepnutí do hexadecimálního režimu. Lze tak jednoduše mapovat znaky a jejich bytovou reprezentaci v souboru. Dále volitelnˇe zalamuje rˇádky v textovém módu a to pˇresnˇe v místˇe, kde rˇádek pˇresáhne vyhrazenou šíˇrku pro prohlížeˇc. Pˇrevádí text mezi nˇekolika vybranými kódováními, já se ovšem pokusím rozšíˇrit tuto množinu na všechna systémem podporované kódování. Pokud se v textu vyskytují sekvence nroff formátování, uživatel muže ˚ zapnout nebo vypnout jejich ˇ odlišení od zbytku textu pˇri zobrazení. Rádek v nroff formátu je však vždy stejnˇe dlouhý nebo kratší jak neformátovaný, a tak se souˇradnice (ˇrádek a sloupec) musí poˇcítat zvlášt’ pro formátovaný a neformátovaný text. A na konec, prohlížeˇc si neukládá nikdy celý soubor do pamˇeti, naopak vˇetšinou si uchovává jen nˇekolik znaku, ˚ maximálnˇe jeden rˇádek pˇri hledání rˇetˇezce. Pomˇernˇe cˇ asto potˇrebuje zjistit rˇádek a sloupec urˇcitého znaku v textu. Pˇri každém hledání souˇradnic by musel projít celý soubor od zaˇcátku, což by znamenalo citelné zpomalení odezvy aplikace. Proto si uchovává pole cˇ tveˇric (pˇríklad 3.3), skládajících se z pozice znaku v souboru, rˇádku a sloupce ve formátovaném i neformátovaném zobrazení. Když chce znát souˇradnice znaku, podívá se nejprve do tohoto pole. Pokud pozici znaku v poli najde, získá souˇradnice pˇrímo z nalezené cˇ tveˇrice. Pokud ne, vezme cˇ tveˇrici s nejbližší pozicí k hledané a pˇresné souˇradnice dopoˇcítá. Pˇritom samozˇrejmˇe cˇ te ze souboru, ale jen jeho nepatrnou cˇ ást. ˇ Novou cˇ tveˇrici pak pˇridá do pole. Casem muže ˚ nastat extrémní situace, kdy pole bude obsahovat cˇ tveˇrici pro každý znak. Pamˇet’ová nároˇcnost tak výraznˇe vzroste a to daleko více, než kdyby si prohlížeˇc uchovával v pamˇeti celý soubor po rˇádcích. Naštˇestí k tomu témˇerˇ nikdy nedochází. Kód prohlížeˇce pˇri cˇ tení souboru spoléhá na to, že jeden znak odpovídá jednomu bytu a že nezáleží na smˇeru cˇ tení. Což zrovna neplatí pro všechna kódování. Také cˇ asto opakovanˇe získává pˇredchudce ˚ a následovníka právˇe zpracovávaného znaku z textu. Proto jsem upravil zpusob ˚ cˇ tení ze souboru. Zjistil jsem, že pro všechny operace si staˇcí pamatovat cˇ tyˇri 20
3. Ú PRAVY
struct str_buffer { struct str_buffer *next; int used; char *data; size_t size; char *actual; size_t remain; };
// // // // // //
PRO
UTF-8
odkaz na další str_buffer zda je volný nebo použitý odkaz na uložený ˇ retˇ ezec ezce retˇ maximální velikost ˇ odkaz na konec ˇ retˇ eze zbývající volné místo
Pˇríklad 3.1: struktura str_buffer
struct hotkey_t { char *start; // ˇ cást pˇ red horkou klávesou char *hotkey; // horká klávesa cást za horkou klávesou char *end; // ˇ }; Pˇríklad 3.2: struktura hotkey_t
struct coord_cache_entry { offset_type cc_offset; offset_type cc_line; offset_type cc_column; offset_type cc_nroff_column; }; Pˇríklad 3.3: struktura coor_cache_entry
21
3. Ú PRAVY
PRO
UTF-8
po sobˇe jdoucí znaky: aktuální, pˇríští, minulý a pˇredminulý. Jelikož prochází text vždy sekvenˇcnˇe, staˇcí v každém cyklu naˇcíst pouze jeden znak ze souboru a ostatní posunout o pozici dál. Napˇríklad z aktuálního se tak stane znak minulý. Navíc se znak pˇrevede do kódování rozhraní pro rˇetˇezce ještˇe pˇred jeho zpracováním. Soubor už však nelze cˇ íst pozpátku. To znamená, že první skok na konec velkého souboru zabere více cˇ asu, nebot’ se projde celý soubor od zaˇcátku. Nejvˇetší komplikace mi ovšem zpusobilo ˚ zalamování rˇádku. ˚ Pˇresnˇeji pˇrípad, kdy se rˇádek rozdˇelí pˇresnˇe na širokém znaku, tedy šíˇrka jeho první cˇ ásti bude o jedno pole menší než by mˇela být. Ale všechny funkce prohlížeˇce spoléhají na konstantní velikost rˇádku˚ pˇri zalomení. Nejjednodušší rˇešení nahrazením širokého znaku dvˇema mezerami jsem si ponechal v záloze a hledal jsem lepší metodu. Zaˇcal jsem uvažovat o nové struktuˇre, ve které by se uchovávaly pomocné informace o jednom rˇádku jako jeho zaˇcátek a konec v souboru nebo celková šíˇrka. Dále by obsahovala jednotlivé podˇrádky, jež by odpovídaly zalomení textu. Všechny struktury by se ukládali do jednoho, respektive dvou zˇretˇezených seznamu, ˚ jeden pro formátovaný a druhý pro neformátovaný text. Již pˇri pokusné implementaci jsem však narazil na nˇekteré nedokonalosti tohoto návrhu. Naˇctení souboru do seznamu se obešlo bez komplikací na rozdíl od manipulace s ním. Procházení seznamu rˇádku˚ s vnoˇreným seznamem podˇrádku˚ nepatˇrilo zrovna mezi jednoduché a pˇrehledné cˇ innosti. Rozhodl jsem se zrušit dˇelení na rˇádky a podˇrádky a sjednotil jsem je do jediné struktury cache_line (pˇríklad 3.4), reprezentující už jen podrˇádky. Obsahuje nˇekolik duležitých ˚ informací o podˇrádku. Za prvé cˇ íslo rˇádku, jenž slouží jako jediná identifikace, zda podˇrádky naleží do stejného rˇádku nebo ne. Dále pozici zaˇcátku a konce poˇrádku v souboru. Konec jednoho podˇrádku se vždy rovná zaˇcátku následujícího. Šíˇrka podˇrádku udává jeho velikost na terminálu. Pokud se rˇádek zalomí na znaku tabulátor, který se nahrazuje až osmi mezerami, odsadí se zbylá cˇ ást o pˇrebývající poˇcet mezer. Tedy si musí pamatovat ještˇe odsazení podˇrádku. Ale i se seznamem podˇrádku˚ se pracuje pomˇernˇe složitˇe a proto jsem napsal nˇekolik pomocných rutin pro typické úkoly, jako napˇr. najdi zaˇcátek cˇ i konec celého rˇádku nebo pˇrejdi na další celý rˇádek. Ještˇe jsem pˇridal funkci pro výpoˇcet souˇradnic znaku tak, aby seznam podˇrádku˚ plnˇe nahradil pole cˇ tveˇric, i když k tomu nebyl puvodnˇ ˚ e urˇcen. Nejdˇríve nalezne podˇrádek, ve kterém se znak nachází. Získá tak cˇ íslo rˇádku a sloupec již snadno dopoˇcítá pˇrímo z odpovídající cˇ ásti souboru. Aby se zabránilo zdržení pˇri otevírání velkého souboru, plní se seznam podˇrádku˚ postupnˇe až bˇehem cˇ tení. Seznam ovšem na rozdíl od puvodního ˚ pole neposkytuje stejnˇe rychlý pˇrístup k libovolnému prvku. Proto si prohlížeˇc uchovává ještˇe odkaz na první zobrazený podˇrádek. Vˇetšinu operací totiž provádí v blízkosti zobrazeného textu. Provedl jsem nˇekolik zkoušek nové implementace a nezaznamenal jsem žádné zpomalení oproti originální verzi. Navíc nyní vyhledává regulární výrazy i v nroff formátovaném textu. To pu˚ vodní prohlížeˇc nedokázal. Zvažoval jsem též optimalizaci pˇri vykreslování dlouhých rˇádku˚ pˇri vypnutém zalamování. Jelikož zná pˇresné rozmˇery jednotlivých cˇ ástí, pˇreskoˇcil by ty, jež se urˇcitˇe nezobrazí. Ovšem kvuli ˚ složitˇejším podmínkám se žádné podstatné zrychlení neuskuteˇcnilo. V ostatních pˇrípadech, jež však tvoˇrí vˇetšinu, došlo spíše ke zpomalení. Nezbylo mi nic jiného, než tento pokus zavrhnout.
22
3. Ú PRAVY
PRO
UTF-8
3.2.14 mc-40-ncurses Pˇri programování úprav jsem vubec ˚ nepoˇcítal pro zobrazení s knihovnou Ncurses. Naštˇestí jsem se vyvaroval pˇrímému volání jakékoliv funkce z knihovny S-Lang, takže vykreslování zustalo ˚ v podstatˇe funkˇcní i s knihovnou Ncurses. Jen jsem detekci knihovny rozšíˇril o kontrolu podpory Unicode. To pˇrípad kdy se v systému vyskytuje nˇekolik ruzných ˚ variant této knihovny. 3.2.15 mc-fullwidth-buttonbar Doplnující ˇ úprava pruhu tlaˇcítek. Napsal jsem ji až po dokonˇcení samotné práce a tak se nestala její souˇcástí. Podle mˇe však poskytuje vizuálnˇe nejlepší provedení pruhu tlaˇcítek. Pruh nyní pokaždé zabírá opravdu celou šíˇrku terminálu. Tím pádem nezustala ˚ všechna stejnˇe velká, ale první tlaˇcítka jsem prodloužil o jedno pole oproti zbytku. 3.2.16 Ostatní úpravy Zbylé modifikace provádˇejí nˇekolik opakujících se zmˇen v ruzných ˚ cˇ ástech kódu mc. Zkontroloval jsem každý výskyt funkce strlen. V pˇrípadˇe, že její volání sloužilo k urˇcení šíˇrka textu na terminálu, nahradil jsem ji funkcí str_term_width1. První UTF-8 patch mi hledání výraznˇe usnadnil, jelikož vˇetšina tˇechto volání strlen v nˇem byla už nahrazena podobnou funkcí mbstrlen. Obˇcas jsem narazil na místa, která se o znaky mimo ASCII vubec ˚ nezajímala nebo je úplnˇe zakazovala. Každé jsem samostatnˇe posoudil a rozhodl se, jestli vyžaduje nˇejakou úpravu. Vˇetšinou jsem jen nahradil puvodní ˚ funkci jinou urˇcenou pˇrímo pro ASCII, jež znaky mimo ASCII vždy ignoruje.
3.3
Aplikace
Tím jsem vyˇcerpal všechny navrhované zmˇeny v programu mc tak, aby bezchybnˇe pracoval v Unicode prostˇredí. Všechny úpravy by mˇely být aplikovány v poˇradí podle jejich oˇcíslování, i když vˇetšinu lze prohodit nebo i vynechat. Ke dvˇema úpravám jsou ještˇe pˇriloženy skripty, které konvertují soubory nápovˇed a tipu˚ do UTF-8.
23
3. Ú PRAVY
PRO
UTF-8
struct cache_line { long number; struct cache_line *prev; struct cache_line *next; offset_type start; offset_type end; screen_dimen width; screen_dimen left; };
// // // // // // //
ˇ císlo ˇ rádku pˇ redchozí podˇ rádek následující podˇ rádek zaˇ cátek v souboru konec v souboru šíˇ rka podˇ rádku odsazení podˇ rádku
Pˇríklad 3.4: struktura cache_line
24
Kapitola 4
Závˇer Mnou navržené úpravy mc jej pˇripraví pro nasazení do prostˇredí se znakovou sadou Unicode v kódování UTF-8. Jeho základní funkcionalitu se mi podaˇrilo zachovat a cˇ ásteˇcnˇe i rozšíˇrit. Opravil jsem vykreslování všech ovládacích prvku˚ uživatelského rozhraní. Nahradil jsem systémové operace pro prohledávání a porovnání rˇetˇezcu. ˚ Jedinou nemodifikovanou cˇ astí mc zustal ˚ editor textových souboru. ˚ Pˇredpokládám ale, že úpravy editoru pujdou ˚ podobným smˇerem jako v pˇrípadˇe prohlížeˇce a textového pole. Tedy vše je již nachystáno a zbývá pouze editor pˇrepracovat a nic principiálnˇe nového v editoru neoˇcekávám. Do té doby doporuˇcuji používat modifikovaný editor, jenž poskytuje pu˚ vodní UTF-8 patch. Vedle editoru zbývá dopsat podporu pro nˇekterá kódování. I v tomto pˇrípadˇe je vše pˇripraveno a cˇ ást již dokonce vyzkoušena. Zaslal jsem již svoji práci vývojáˇrum ˚ mc a ti ji pˇrijali velmi dobˇre. Nikdo doposud nemˇel žádné výhrady, jen si všimli nˇekolika drobných chyb, které jsem rychle odstranil. Vypadá to, že celá práce skonˇcí úspˇešnˇe a bude zaˇclenˇena do hlavního kódu mc.
25
Literatura [1] Zeyd M. Ben-Halim, Eric S. Raymond, and Thomas E. Dickey. Ncurses manual page [online]. Dostupný z WWW
, 2007. [2] The Unicode Consortium. The Unicode Standard, Version 5.0. Addison-Wesley Professional, 12 2006. [3] J. E. Davis. S-Lang Library Information Page [online]. Dostupný z WWW , 2007. [4] GNU Midnight Commander [online]. Dostupný z WWW , 2007. [5] Goran Rakic. GLib Reference Manual [online]. Dostupný z WWW , 2007.
27
Dodatek A
Popis rozhraní pro rˇ etˇezce I: funkce správnˇe zachází s neplatnými sekvencemi, nedojde nikdy k chybˇe. L: funkce se implementaˇcnˇe liší pro ruzná ˚ kódování.
str_init_strings použití: void str_init_strings (const char *termenc) popis: Vybere správnou sadu funkcí pro systémové nebo zadané kódování. Posléze inicializuje základní konvertory a seznam rostoucích rˇetˇezcu. ˚
str_uninit_strings použití: void str_uninit_strings () popis: Zavˇre základní konvertory a uvolní všechny rostoucí rˇetˇezce, i doposud používané.
str_cnv_to_term deklarace: iconv_t str_cnv_to_term popis: Základní konvertor, který pˇrevádí text z vnitˇrního kódování do systémového. Jelikož v aktuální verzi jsou vždy obˇe shodné, nemá praktické využití. V následujících verzích se pˇredpokládá opˇetovné využití.
str_cnv_from_term deklarace: iconv_t str_cnv_to_term popis: Základní konvertor, který pˇrevádí text ze systémového kódování do vnitˇrního. Jelikož v aktuální verzi jsou vždy obˇe shodné, nemá praktické využití. V následujících verzích se pˇredpokládá opˇetovné využití. 29
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_cnv_not_convert deklarace: iconv_t str_cnv_not_convert popis: Základní konvertor, jenž zachovává pˇri konverzi puvodní ˚ text. Neprovádí se žádná konverze, text se pouze zkopíruje.
str_crt_conv_to použití: iconv_t str_crt_conv_to (const char *to_enc) popis: Vytvoˇrí konvertor, který pˇrevádí texty z vnitˇrního do zadaného kódováním. Pokud jsou obˇe shodné, vrátí str_cnv_not_convert. Když nelze konvertor vytvoˇrit, vrátí INVALID_CONV.
str_crt_conv_from použití: void str_crt_conv_from (const char *from_enc) popis: Vytvoˇrí konvertor, jenž pˇrevádí texty ze zadaného do vnitˇrního kódováním. Pokud jsou obˇe shodné, vrátí str_cnv_not_convert. Když se konvertor nepodaˇrí vytvorˇit, vrátí INVALID_CONV.
str_close_conv použití: void str_close_conv (iconv_t conv) popis: Zruší konvertor. Každý nepotˇrebný konvertor musí být uzavˇren. Jedinˇe základní konvertory str_not_convert, str_cnv_to_term, str_cnv_from_term nesmí být odstranˇeny.
struct str_buffer deklarace: struct str_buffer { struct str_buffer *next; int used; char *data; size_t size; char *actual; size_t remain; }; popis: Struktura uchovávající rˇetˇezec s promˇenou délkou. Nedoporuˇcuje se mˇenit pˇrímo jakoukoliv její položku, ale použít k tomu urˇcených funkcí. 30
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_get_buffer použití: struct str_buffer *str_get_buffer () popis: Vrátí volný nebo vytvoˇrí nový str_buffer, jenž pˇredstavuje rˇetˇezec s promˇenou délˇ ezec je pˇrímo pˇrístupný pod položkou data. kou. Retˇ
str_release_buffer použití: void str_release_buffer (struct str_buffer *buffer) popis: Vynuluje a vrátí buffer do seznamu volných. Bude opˇet použit, až jej získá jiná funkce voláním str_get_buffer.
str_reset_buffer použití: void str_reset_buffer (struct str_buffer *buffer) popis: Vynuluje rˇetˇezec ve struktuˇre buffer.
str_convert použití: int str_convert (iconv_t coder, char *string, struct str_buffer *buffer) popis: Pˇrevede konvertorem coder rˇetˇezec string a výsledek zapíše na konec rostoucího rˇetˇezce buffer. Vrací 0, pokud dopadla konverze úspˇešnˇe, ESTR_PROBLEM, když nˇekteré znaky nebylo možné pˇrevést a ESTR_FAILURE, když došlo k chybˇe.
str_vfs_convert_from použití: int str_vfs_convert_from (iconv_t coder, char *string, struct str_buffer *buffer) popis: Speciální varianta str_convert pro pˇrevod jména souboru do vnitˇrního kódování. Výsledek je možné pˇreložit nazpˇet pouze pokud nedošlo k žádnému problému a funkce vrátí hodnotu nula.
str_vfs_convert_to (I, L) použití: int str_vfs_convert_to (str_conv_t coder, const char *string, int size, struct str_buffer *buffer) 31
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
popis: Speciální varianta str_convert pro pˇreklad jména a cesty k souboru zpátky do kódování systému. Pokud adresáˇr v cestˇe nebo jméno souboru obsahuje neplatnou UTF-8 sekvenci, pˇríslušná cˇ ást se ponechá v puvodním ˚ stavu. Nevzniknou tak zkomolené názvy z obou kódování.
str_incrase_buffer použití: void str_incrase_buffer (struct str_buffer *buffer) popis: Zvˇetší kapacitu rˇetˇezce ve struktuˇre buffer. Zpravidla se pˇrizpusobuje ˚ automaticky a není nutné tuto funkci volat pˇrímo.
str_insert_char použití: void str_insert_char (char ch, struct str_buffer *buffer) popis: Pˇridá na konec jeden jednobytový znak. Vhodné pro znaky ze sady ASCII.
str_insert_string (I) použití: void str_insert_string (const char *string, struct str_buffer *buffer) void str_insert_string2 (const char *string, int size, struct str_buffer *buffer) popis: Vloží na konec rostoucího rˇetˇezce buffer obsah textu string. Varianta 2 omezuje velikost vkládaného rˇetˇezce.
str_printf použití: void str_printf (struct str_buffer *buffer, const char *format, ...) popis: Alternativa funkce printf pro rostoucí rˇetˇezce. Výsledek uložen na konec.
str_insert_replace_char (L) použití: void str_insert_replace_char (struct str_buffer *buffer) popis: Vloží znak nebo sekvenci používanou vnitˇrním kódováním pˇri náhradˇe znaku. 32
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_backward_buffer použití: void str_backward_buffer (struct str_buffer *buffer, int count) popis: Smaže poslední znak v rostoucím rˇetˇezci.
str_translate_char (L) použití: int str_translate_char (str_conv_t conv, char *ch, size_t ch_size, char *output, size_t out_size) popis: Zkusí pˇrevést jeden znak a zapsat jej do rˇetˇezce output. Vrátí 0 pokud uspˇela. V pˇrípadˇe neúplné sekvence vrací ESTR_PROBLEM, v pˇrípadˇe neplatné sekvence vrací ESTR_FAILURE.
str_is_valid_string (I, L) použití: int str_is_valid_string (const char *text) popis: Zjistí zda rˇetˇezec ve vnitˇrním kódování neobsahuje chyby.
str_is_valid_char (I, L) použití: int str_is_valid_char (const char *ch, size_t size) popis: Zjistí, zda první znak rˇetˇezce je v poˇrádku. Pokud ano, vrací 1, pro neúplnou sekvenci -2 a pro neplatnou sekvenci -1.
str_toupper (L) použití: int str_toupper (const char *ch, char **out, size_t *remain) popis: Pˇrevede znak na malý. Vrací, zda byla funkce úspˇešná.
str_tolower (L) použití: int str_tolower (const char *ch, char **out, size_t *remain) popis: Pˇrevede znak na velký. Vrací, zda byla funkce úspˇešná. 33
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_next_char (L) použití: char *str_get_next_char (char *text) const char *str_cget_next_char (const char *text) void str_next_char (char **text) void str_cnext_char (const char **text) popis: Pˇrejde nebo vrátí zaˇcátek následujícího znaku. Neprovádí se žádná kontrola. Žádná z variant nesmí být volána s odkazem na konec rˇetˇezce. Nepodporuje neplatné sekvence
str_prev_char (L) použití: char *str_get_prev_char (char *text) const char *str_cget_prev_char (const char *text) void str_prev_char (char **text) void str_cprev_char (const char **text) popis: Pˇrejde nebo vrátí zaˇcátek pˇredcházejícího znaku. Neprovádí se žádná kontrola. Ani jedna z variant nesmí být volána s odkazem na zaˇcátek rˇetˇezce. Nepodporuje neplatné sekvence
str_next_char_safe (I, L) použití: char *str_get_next_char_safe (char *text) const char *str_cget_next_char_safe (const char *text) void str_next_char_safe (char **text) void str_cnext_char_safe (const char **text) popis: Rozšiˇrují funkce ze str_next_char. Kontrolují ale i pˇrítomnost neplatných sekvencí.
str_prev_char_safe (I, L) použití: char *str_get_prev_char_safe (char *text) const char *str_cget_prev_char_safe (const char *text) void str_prev_char_safe (char **text) void str_cprev_char_safe (const char **text) popis: Rozšiˇrují funkce ze str_prev_char. Kontrolují ale i pˇrítomnost neplatných sekvencí. 34
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_next_noncomb_char (I, L) použití: int str_next_noncomb_char (char **text) int str_cnext_noncomb_char (const char **text) popis: Dále rozšiˇrují funkce ze str_next_char_safe. Pˇreskoˇcí však všechny kombinaˇcní znaky. Vrací poˇcet vynechaných znaku˚
str_prev_noncomb_char (I, L) použití: int str_prev_noncomb_char (char **text, const char *begin) int str_cprev_noncomb_char (const char **text, const char *begin) popis: Dále rozšiˇrují funkce ze str_prev_char_safe. Pˇreskoˇcí však všechny kombinaˇcní znaky. Vrací poˇcet vynechaných znaku. ˚ Nikdy nepˇrekroˇcí zadaný zaˇcátek rˇetˇezce.
str_is... (I, L) použití: int int int int int
str_isspace str_ispunct str_isalnum str_isdigit str_isprint
(const (const (const (const (const
char char char char char
*ch) *ch) *ch) *ch) *ch)
popis: Zjistí, zda první znak v rˇetˇezci je prázdný, interpunkce, cˇ íslo cˇ i písmeno. Též zda má pˇriˇrazenu standardní grafickou podobu.
str_iscombiningmark (I, L) použití: int str_iscombiningmark (const char *ch) popis: Testuje, jestli je na zaˇcátku rˇetˇezce kombinaˇcní znak.
str_length (I, L) použití: int str_length (const char* text) int str_length2 (const char* text, int size) popis: Vrací poˇcet znaku˚ v rˇetˇezci. Varianta 2 poˇcítá s omezenou délkou. 35
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_length_noncomb (I, L) použití: int str_length_noncomb (const char* text) popis: Vrací poˇcet nekombinaˇcních znaku˚ v rˇetˇezci.
str_fix_string (I, L) použití: void str_fix_string (char* text) popis: Nahradí všechny neplatné sekvence otázníky. Velikost rˇetˇezce se nemˇení.
str_term_form (I, L) použití: const char *str_term_form (const char *text) popis: Pˇrevede rˇetˇezec do podoby vhodné pro zobrazení. Nahradí všechny netisknutelné znaky teˇckou a neplatné sekvence nahrazovacím znakem. Pokud text zaˇcíná kombinaˇcním znakem, pˇridá se pˇred nˇej mezera.
zarovnání Varianty FIT provádí zkrácení znakem ˜. J_LEFT, J_LEFT_FIT zarovnání k levém kraji J_RIGHT, J_RIGHT_FIT zarovnání k pravém kraji J_CENTER, J_CENTER_FIT zarovnání na stˇred J_CENTER_LEFT, J_CENTER_LEFT_FIT pokud je dostatek prostoru, zarovná se text na stˇred, jinak k levému okraji
str_fit_to_term (I, L) použití: const char *str_fit_to_term (const char *text, int width, int just_mode) popis: Pˇridává k str_term_form volbu zarovnání. Výsledek má vždy požadovanou šíˇrku. Bud’ je zkrácen nebo doplnˇen mezerami. Zkrátí se bud’ nˇekterý kraj rˇetˇezce nebo se jeho prostˇrední cˇ ást nahradí znakem ˜. 36
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_term_trim (I, L) použití: const char *str_term_trim (const char *text, int width) popis: Pokud je rˇetˇezec pˇríliš široký, zachová se jen sufix a zaˇcátek se nahradí tˇremi teˇckami. Pokud ne, doplní se mezerami na požadovanou šíˇrku.
str_msg_term_size (I, L) použití: void str_msg_term_size (const char *text, int *lines, int *columns) popis: Zjistí poˇcet rˇádku˚ a sloupcu˚ textu.
str_term_substring (I, L) použití: const char *str_term_substring (const char *text, int start, int width) popis: Vrátí podˇretˇezec, který zaˇcíná na daném sloupci a má zadanou šíˇrku. Pˇrípadnˇe je krátký výsledek doplnˇen mezerami na požadovanou šíˇrku.
str_term_char_width (I, L) použití: int str_term_char_width (const char *text) popis: Vrací šíˇrku prvního znaku v textu.
str_term_width (I, L) použití: int str_term_width1 (const char *text) int str_term_width2 (const char *text, size_t length) popis: Vrátí šíˇrku rˇetˇezce. Varianta 2 omezuje délku zpracovaného rˇetˇezce. Pokud text zacˇ íná kombinaˇcní znakem, poˇcítá se výjimeˇcnˇe jako normální znak
str_offset_to_pos (I, L) použití: int str_offset_to_pos (const char* text, size_t offset) popis: Pˇrevede offset v bytech do pozice ve znacích pro daný text. 37
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
str_column_to_pos (I, L) použití: int str_column_to_pos (const char *text, size_t pos) popis: Pˇrevede cˇ íslo sloupce do pozice ve znacích pro daný text.
str_trunc (I, L) použití: const char *str_trunc (const char *text, int width) popis: Pokud je text širší než je povoleno, nahradí se jeho prostˇrední cˇ ást ˜.
str_create_search_needle (L) použití: char *str_create_search_needle (const char *needle, int case_sen) popis: Transformuje hledaný rˇetˇezec do vhodnˇejšího tvaru pro rychlejší prohledání.
str_release_search_needle (L) použití: void str_release_search_needle (char *needle, int case_sen) popis: Uvolní již nepotˇrebný transformovaný hledaný rˇetˇezec.
str_str_search (L) použití: const char *str_search_first (const char *text, const char *needle, int case_sen) const char *str_search_last (const char *text, const char *needle, int case_sen) popis: Hledá v textu transformovaný rˇetˇezec. Pokud jej najde, vrátí zaˇcátek rˇetˇezce v textu. V UTF-8 není zaruˇceno, že hledaný rˇetˇezec i nalezený výskyt budou stejnˇe velké nebo budou mít stejný poˇcet znaku. ˚
str_compare (I, L) použití: int str_compare (const char *t1, const char *t2) int str_casecmp (const char *t1, const char *t2) 38
A. P OPIS
ˇ ˇ ROZHRANÍ PRO RET EZCE
popis: Porovnává dva rˇetˇezce. První upˇrednostnuje ˇ velká písmena, druhá velikost nerozlišuje.
str_ncompare (I, L) použití: int str_ncompare (const char *t1, const char *t2) int str_ncasecmp (const char *t1, const char *t2) popis: Porovnává dva rˇetˇezce. První upˇrednostnuje ˇ velká písmena, druhá velikost nerozlišuje. Pokud je jeden z rˇetˇezcu˚ prefixem druhého, vyhodnotí je jako shodné.
str_prefix (I, L) použití: int str_prefix (const char *text, const char *prefix) int str_caseprefix (const char *text, const char *prefix) popis: Vrátí, jak dlouhý je spoleˇcný prefix obou rˇetˇezcu. ˚ Výsledek je ovšem poˇcet znaku˚ odpovídají nikoliv puvodním ˚ rˇetˇezcum ˚ ale jejich normalizovanému tvaru.
str_create_key (I, L) použití: char *str_create_key (const char *text, int case_sen) char *str_create_key_for_filename (const char *text, int case_sen) popis: Vytvoˇrí pro rˇetˇezec porovnávací klíˇc. Klíˇce lze rychleji porovnávat než puvodní ˚ rˇetˇezce. Speciální varianta pro jména souboru˚ zohlednuje ˇ vždy teˇcku i pˇri lokalizovaném srovnání, jinak bývá vˇetšinou zapomenuta. Pro nˇekterá kódování se klíˇc nemusí vytváˇret a funkce vrací pˇrímo zadaný rˇetˇezec.
str_key_collate (I, L) použití: int str_key_collate (const char *t1, const char *t2, int case_sen) popis: Porovná dva klíˇce. Klíˇce jsou rychlejší pˇri opakovaném porovnávání rˇetˇezcu, ˚ než použití str_compare.
str_release_key (I, L) použití: void str_release_key (char *key, int case_sen) popis: Uvolní porovnávací klíˇc. Ale jen v pˇrípadˇe, že klíˇc je odlišný od puvodního ˚ rˇetˇezce.
39