Kopírování a přesouvání adresářů v Calm Commanderu
Dnes jsem vydal Calm Commander 1.0, ale o tom tento článek není - byť by si to článek zasloužilo, ale to necháme na jindy.... Calm Commander totiž umí konečně kopírovat adresáře, včetně podadresářů a jejich podpodadresářů a jejich podpodpodadresářů. Jelikož je v Calm Commanderu hodně málo místa odkládal jsem tuto funkcionalitu dokud to šlo. Ale když jsem minulý týden zavedl něco, čemu se říká pluginy (na prohlížení txt souborů, nebo obrázků, poslouchání hudby...), řekl jsem si, proč neudělat rovnou systémový plugin, který bude řešit rekurzivní kopírování souborů. Takže místo toho, aby se kód vykonával v hlavní paměti (i když nevím, kde bych ji popravdě vzal), místo toho se zavolá externí systémový plugin syscopy.ccp. Hlavní program pouze připraví kontext, nahraje plugin do paměti, předá mu zdrojovou cestu, cílovou cestu, jméno adresáře a několik služeb, přes které může plugin komunikovat zpět s Calm Commanderem.
Hloubka kopírování
Plugin má pevně nastavenou maximální hloubku stromu pomocí konstanty MAX_DEPTH = 11. Vybraný adresář se bere jako hloubka 0, jeho podadresáře jsou hloubka 1, další úroveň je hloubka 2 a tak dál. Pro cesty jsou připravené sloty pro hloubky 0 až 11, tedy celkem 12 úrovní včetně vybraného kořenového adresáře. Prakticky to znamená, že vybraný adresář se v cíli vytvoří jako úroveň 0, jeho obsah se rekurzivně zpracovává dále a položky na úrovni 11 lze ještě vytvořit nebo zkopírovat. Obsah adresáře, který už leží na úrovni 11, se ale dál neprochází.
Plugin si dopředu nevytváří žádnou uloženou stromovou strukturu v paměti. Adresářový strom zjišťuje průběžně čtením adresářů. Nejdřív sestaví zdrojovou cestu, potom cílovou cestu, vytvoří aktuální adresář v cíli a otevře zdrojový adresář. Následně čte položky jednu po druhé. Podle atributu adresáře pozná, jestli jde o soubor nebo podadresář. Soubor se zkopíruje okamžitě. Pokud jde o adresář, plugin sestaví cesty dítěte a zavolá stejnou kopírovací rutinu rekurzivně. Jde tedy o klasický DFS průchod (Depth-First Search), tedy průchod do hloubky. Plugin vstoupí do adresáře a čte jeho položky jednu po druhé. Soubor zkopíruje okamžitě. Jakmile narazí na podadresář, vytvoří jeho cílový protějšek a hned do něj sestoupí. Po návratu pokračuje další položkou původního adresáře. Není to postup, kdy by se nejprve vytvořil celý strom adresářů a až potom se kopírovaly soubory. Všechno probíhá průběžně.
Plugin běží v paměti od adresy $C000 a pracovní prostor má od $E000. Hlavní program mu namapuje zvláštní banku pro kód pluginu a samostatnou pracovní stránku pro data. Pro zdrojové a cílové cesty používá plugin pevně dané bloky paměti. Zdrojové cesty začínají na $E000, cílové cesty na $EC00. Každá hloubka má pro zdrojovou cestu 256 bajtů a pro cílovou cestu dalších 256 bajtů. Hloubka 0 tedy používá první slot, hloubka 1 další slot a tak dále. Díky tomu plugin nemusí alokovat paměť dynamicky. Stačí mu spočítat adresu podle aktuální hloubky a pracovat s odpovídajícím 256b blokem. Cesty skládá rutina, která vezme cestu rodiče, podle potřeby přidá lomítko a připojí jméno potomka. Umí pracovat s terminátorem 0 i 255. Pokud by výsledná cesta přesáhla dostupnou délku, plugin operaci ukončí chybou.
Pro práci s adresáři plugin používá ESXDOS volání (možná z tohoto kopírování udělám časem dot command, tak abych si to do budoucna nekomplikoval). Adresář otevře, čte z něj jednotlivé položky a po dokončení ho zavře. Pro soubory používá otevření zdroje pro čtení a cíle pro zápis s přepsáním původního obsahu. Samotné kopírování souboru uvnitř adresáře je proudové. Plugin čte bloky o velikosti 2048 bajtů do kopírovacího bufferu a hned je zapisuje do cílového souboru. Buffer je sdílený s pamětí pro adresářovou položku, protože v okamžiku kopírování konkrétního souboru se zrovna nečte další položka adresáře.
Zajímavá část implementace je práce s otevřenými handly adresářů. Plugin nedrží otevřený handle pro každý adresář v celé rekurzi. To by jsme rychle narazili na limit otevřených handlů (na což mě upozornil ub880d). Místo toho otevře aktuální adresář a začne číst položky. Když narazí na podadresář, uloží si počet už přečtených položek v dané hloubce, zavře handle rodičovského adresáře a rekurzivně zpracuje potomka. Po návratu z rekurze rodičovský adresář znovu otevře a znovu přečte začátek adresáře. Už zpracované položky zahodí, dokud se nedostane na uloženou pozici. Pak pokračuje dál.
Přesun adresáře
Přesun adresáře není řešen jako obyčejný rename. Plugin provede nejdřív kopírovací fázi a po jejím úspěšném dokončení spustí mazací fázi původního stromu. Mazání probíhá opačně než kopírování. Nejdřív se smažou soubory a podadresáře a teprve potom aktuální adresář. Je to bottom-up postup, který zaručuje, že adresář se maže až ve chvíli, kdy je prázdný.
Ochrana proti kopírování do sebe sama
Plugin obsahuje i ochranu proti situaci, kdy by se adresář kopíroval sám do sebe. Před spuštěním kopie porovná zdrojovou a cílovou cestu. Pokud je cíl stejný jako zdroj, nebo pokud cílová cesta leží uvnitř zdrojové cesty, operace se odmítne. Bez této kontroly by mohlo dojít k nekonečnému zanořování, kdy by se cílový adresář stal součástí stromu, který se právě kopíruje.
Přepis existujících souborů
Při kopírování souborů uvnitř adresáře plugin nejdřív zkusí otevřít cílový soubor pro čtení. Pokud se otevření povede, znamená to, že soubor už existuje. V takovém případě se plugin neptá uživatele sám, ale zavolá službu hlavního programu. Díky tomu se použije stejný overwrite dialog jako při běžném kopírování souborů. Plugin tedy neřeší uživatelské rozhraní přímo.
Zítra si řekneme co vše je nové v Calm Commanderu 1.0, protože kopírování adresářů není to jediné co jsem tam přidal. A jak tak píši ten článek, tak tečkovaný příkaz na kopírování souborů/adresářů asi není úplně špatnej nápad.

