Jó volt, szép volt.
Kiemelt támogatók
BIG FISH
Pentaschool
WISH
Macasoft
G'Roby
23vnet
Kiskapu
 

  5k – górcső : zsview

screenshotelkövette: Szabó Péter
LEÍRÁS


Leírás

Az ,,Enter ZIPfile URL:'' mezőbe először azt kell írni, hogy

	minta.zip

, később pedig tetszőleges URL írható, ami ZIP file-t tartalmaz.

Ez a könyvtár egy pályamű a 2003 tavaszán megrendezett magyar PHP
konferencia
(http://phpconf.hu) 5k compo című versenyére.

Szerző: Szabó Péter
A szerző e-mail címe: pts+phpconf##kukac##math.bme.hu
A pályamű fantázia(?)neve: zsview -- ZIP Source Viewer
A pályamű keletkezési ideje: 2003. március 10.
A részletes dokumentáció keletkezési ideje: 2003. március 17.

Telepítés, indítás
~~~~~~~~~~~~~~~~~~
Az 5120 nevű könyvtárban levő file mérete 5120 byte. Ezt a file-t
(zsview.php) kell feltenni egy olyan webszerverre, ahol PHP4 fut (4.2.2-vel
és 4.3.1-gyel tesztelve). Érdemes még felmásolni az eggyel feljebbi
könyvtárban levő minta.zip-t (a webszerveren a zsview.php-val egyező
könyvtárba). Ezután egy webböngészőben a zsview.php file-t kell
megtekinteni.

A kibontandó .zip file-t akárhol elhelyezhetjük a weben, ekkor a teljes,
,,http://''-vel kezdődő URL-t meg kell adni a szövegmezőben.

A program PHP CLI SAPI-ból nem működik jól, mert a $_GET tömbben vár
paramétereket. Ha a felhasználó el tudja érni, hogy a $_GET tömbben épp a
megfelelő értékek legyenek, akkor ,,php zsview.php'' paranccsal is
futtathatja a programot. A program a szabványos kimenetére ekkor egy HTML
file-t ír, amit átirányítható és webböngészőből megtekinthető.

Licensz: GNU GPL >=2.0

Mit tud?
~~~~~~~~
A zsview egy ZIP archívban levő file-ok nevét képes kilistázni, és a
file-ok
tatartalmát megmutatni. Az alábbi file-formátumok támogatottak:

-- HTML (.html és .htm)
-- XML, ISO-8859-[12] kódolással (.xml)
-- Java forrás (.java és .jad)
-- PHP forrás (.php, .phps és .inc)
-- az egyéb kiterjesztésű file-ok sima szövegfile-ként jelennek meg,
UNIX-os
   vagy DOS-os soremelés van támogatva

HTML, XML, Java és PHP esetén a file-t színezve (syntax highlighting)
jelenik meg.

Tehát a program tudása:

-- ZIP file-ok listázása
-- ZIP (Flate) kitömörítő implementáció PHP nyelven
-- HTML, XML szintaxis-színezés
-- Java szintaxis-színezés
-- PHP szintaxis-színezés a highlight_string() függvénnyel

Hibakezelés nincs (nem fért bele).

Mi ebben a pláne?
~~~~~~~~~~~~~~~~~
Az egész script 5120 byte.

Tájékoztatásul a PKZIP csomag részét képező pkunzjr.com-ot említem, ami
DOS alatt fut, 3000 byte-nál rövidebb, de nem tud szintaxis-színezést. Ez
az
összehasonlítás nem objektív, mert egy PHP nyelven írt forráskód
hosszát nem lehet összehasonlítani egy assembly nyelven írt program
binárisával.

A forráskód olvasóinak figyelmébe
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A zsview.php nehezen olvasható, mert a méretkorlátozás miatt kitöröltem a
megjegyzéseket és a fölösleges szóközöket, továbbá egybetűs változó-,
függvény-, és HTTP GET paraméterneveket használok. Ebben a szakaszban a
forráskód értelmezéséhez adok segítségét.

A zsview.php melletti minta.zip-ben megtalálható maga a zsview.php, tehát a
programmal a saját forráskódja könnyen megjeleníthető szintaxis-színezve.
Ez nagyban segítheti a megértést, mert könnyebb behatárolni a stringeket,
és
egyéb lexikai elemeket.

A Flate kitömörítést az alábbi IETF szabvány alapján végzem:

	DEFLATE Compressed Data Format Specification version 1.3 (1996)
	ftp://ftp.rfc-editor.org/in-notes/rfc1951.txt

Érdekesek még ezek a szabványok:

	ZLIB Compressed Data Format Specification version 3.3
	ftp://ftp.rfc-editor.org/in-notes/rfc1950.txt

	GZIP file format specification version 4.3
	ftp://ftp.rfc-editor.org/in-notes/rfc1952.txt

Az RFC 1952-ben olvashatjuk a gzip(1) program által készített .gz file-ok
formátumát. Az RFC 1950-ben olvashatjuk a PostScript és PDF nyelvek
/FlateDecode filterének bemeneti formátumát. Mindkét formátum egyszerű
kiegészítései a Deflate-nek, lényegében csak egyetlen filenév, a file
hossza, a dátum és a CRC az, amit pluszban tárolnak. A zsview.php kezdeti
változata ezeket is ki tudta tömöríteni, de ezt később kihagytam belőle,
részben helyhiány miatt, részben azért, mert a ZIP archívok megjelenítése
sokkal látványosabb.

Az újabb ZIP file-okban (az őskövületnek számító PKZIP 2.0-tól kezdve)
kizárólag a Deflate tömörítés használatos, a mai ZIP tömörítők csak
Deflate-tel tudnak becsomagolni, a kicsomagoláshoz persze --
kompatibilitási
okokból -- számos egyéb algoritmust implementálnak. A zsview csak a
Deflate-et tudja kitömöríteni.

Egy ZIP archív több, Flate-tel tömörített file-t tartalmaz, az itt leírt
formátumban:

	http://www.combatsimulations.com/idc/zip-format.txt

A ZIP egy elég egyszerű formátum, lényegében szekvenciálisan olvasható:
először (local file header, local file)-ok sorozata jelenik meg, majd
(central directory entry)-k sorozata következik, és az (end of central
directory) zárja a file-t. A zsview.php számára csak a local file header
érdekes, ebből olvassa ki a file-ok neveit, és azt, hogy a tömörített adat
a
ZIP melyik offset-jén kezdődik.

A PHP forrásfile-ok szintaxis-színezése viszonylag egyszerű: a beépített
highlight_string() függvényt használom. Sajnos ez a függvény a HTML
file-okat feketén hagyja, ezért HTML-re (és egyben XML-re) saját függvényt
írtam (d). Ez a függvény lényegében karakterenként megy végig a HTML vagy
XML
forráson, megállapítja a betűcsoportok határait (pl. <? xml version =
"1.0"
), és minden betűcsoportot megfelelően színez. A preg_match() függvényt
csak
spórolásra használom, pl. ,,$c=="a" || $c=="b" ||
$c=="c" || $c=="d"''
helyett kézenfelvő ,,preg_match('##kukac##[a-d]##kukac##',$c)''-t írni.

A Java forráskódok színezéséhez már jobban igényben veszem a PREG könyvtár
regexp illesztési lehetőségeit: a c() függvény egyetlen Java token-t színez
az első karaktere alapján. A c()-t a h() függvényben található
preg_replace_callback() hívja meg minden egyes tokenre. A
preg_replace_callback()-nek átatott riasztóan hosszú regexp egyetlen Java
token-re (pl. szám, azonosító, string, megjegyzés) illeszkedik. Ez mutatja
a
Perl-es regexp-ek tömör kifejezőerejét. Remélem, a PHP későbbi verzióiba
nyelvi szinten integrálva lesznek a regexp-ek.

A színezést a h() függvény koordinálja. A HTTP GET kérés "1" nevű
mezőjében
előre meg van adva, hogy milyen típusú a forrásfile, eszerint szétágazik a
megfelelő színezőfüggvényre. Az "1" mezőt a hívó HTML-t előállító
a()
függvény töltötte ki, a t() függvényt hívva, ami egy filenév alapján
eldönti, hogy az milyen típusú. Mind a kulcs ("1") és a
lehetséges értékek
(1, 2, 3 vagy 4) egész számok, ezzel sok idézőjelet megspóroltam.

A függvények részletes leírása
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A főprogram eleje
"""""""""""""""""
Néhány segédtömb kezdeti értékét állítja be.

unsigned r(unsigned bitc);
""""""""""""""""""""""""""
bitc db bitet olvas a bemenetről ($s stream, tömörített adatfolyam), a
biteket egy nemnegatív egész számmá fűzi össze, és azt visszaadja. Azért
van
rá szükség, mert a Flate a tömörített file-t egy bitfolyamnak tekinti,
tehát
a kitömörítéskor biteket kell olvasni. Az r() pont olyan sorrendben olvas,
ahogy azt a Deflate specifikáció megköveteli.

void f(unsigned root);
""""""""""""""""""""""
A kitömörítéshez használt Huffman-fának a root indexű gyökeréből elérhető
teljes részfát felszabadítja.

unsigned e(unsigned root);
""""""""""""""""""""""""""
Biteket olvas a bemenetről, és az olvasott érték alapján lefelé halad a
Huffman-fában. Ha levélhez érkezik, visszaadja a levélben található
karakterkód vagy távolság-információ értékét. A Huffman-kódolás prefix
tulajdonsága garantálja, hogy nem kell előre letárolni az olvasandó
bitek számát, mert az menet közven derül ki a Huffmann-fából.

unsigned m(unsigned len);
"""""""""""""""""""""""""
Huffman-fát allokál és épít, melynek leveleiben a 0, 1, ..., len-1 értékek
(kódszavak) vannak letárolva, a $Z tömbben ($Z[0] ... $Z[len-1]) megadott
gyakoriságban. A Deflate szabvány előírja, hogyan kell
kódszógyakoriságokból
Huffman-fát építeni, az m() függvény eszerint működik. Visszaadja a fa
gyökerének az indexét.

A Huffman-fák belső ábrázolása a következő: a k indexű levél vagy belső
csúcs
a globális $N tömb $N[k], $N[k+1], $N[k+2] elemeiben található. Az $N[0],
$N[1], $N[2] elemek nem használtak. Levél esetén $N[k]==0, $N[k+1], $N[k+2]
pedig a levélhez tartozó érték. Belső csúcs esetén $N[k]>=3, $N[k] a bal
(0
irányú) gyermek indexét, $N[k+1] a jobb (1 irányú) gyermek indexét
tartalmazza, $N[k+2] pedig tetszőleges.

int t(string filename);
"""""""""""""""""""""""
Eldönti a file típusát (formátumát) a neve alapján. HTML, XML -> 4, PHP
->
2, Java -> 1, egyéb -> 0.

void a(bool extract, string selfname);
""""""""""""""""""""""""""""""""""""""
extract==FALSE esetén a $s stream-ből olvasható ZIP file-t listázza ki
HTML-ben. A listában megjeleníti a file-nevet, és egy link-et helyez el,
melynek GET QUERY_STRING mezőjében feltűnteti a file ZIP-beli offset-jét,
és
a szintaxis-színezéshez szükséges file-formátumot, amit a t() függvény
számol ki.

extract==TRUE esetén a $s ZIP stream-ből egyetlen file-t bont ki, és annak
tartalmát írja ki echo-val. Az $s stream-et előzőleg egy local file
header-re 
kell pozícionálni. A ZIP file-ok kibontása az RFC 1951 alapján történik
(Huffman-fa építése és 32768 byte-os körforgó visszanéző puffer),
algoritmikus szempontból meglepő elemet nem tartalmaz. Figyeljük meg, hogy
a
Flate kitömörítést konstans memóriaigénnyel oldja meg a függvény, még a
változó méretű Huffman-fákat is a a fix méretű $N tömbben tárolja, mivel a
fák csúcsainak összszáma sosem lépheti túl a 666-ot.

string b(string c, string s);
"""""""""""""""""""""""""""""
Segédfüggvény a színek kiírásához.

string d(string s);
"""""""""""""""""""
Az ,,s'' string-et írja ki szintaxis-színezett HTML-ként.

string s(string str);
"""""""""""""""""""""
A htmlspecialchars()-ot hívja. Mivel sokszor van rá szükség, ezért a rövid
s() függvénynév előnyösebb.

string h(string s, string dummy);
"""""""""""""""""""""""""""""""""
Az ,,s'' string-et szintaxis-színezi a HTTP GET kérésben leírtaknak
megfelelően.

A főprogram vége
""""""""""""""""
A HTTP GET QUERY_STRING alapján vagy megjeleníti a ZIP file-választó
formot,
vagy meghívja az a() függvényt a listázáshoz, vagy meghívja az a()
függvényt
a kitömörítéshez. Ez utóbbi esetben ob_start()-tal elkapja az a()
kimenetét,
majd a h() függvénnyel elvégezteti a szintaxis-színezést.

A globális változók részletes leírása
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$U $W $Q $L $Z $P
"""""""""""""""""
Segédtáblázatok a Flate kitömörítéshez.

$K
""
Segédtömb, kulcsként a Java kódszavakat tartalmazza.

$s
""
A ZIP file-t tartalmazó stream. Nem szabad seek-elni benne, mert lehet,
hogy
HTTP vagy egyéb URL, amit előrefelé olvasható. Épp ezért gyakori, hogy egy
fread() hívás eredményét eldobom.

$Y
""
A Flate kitömörítés során már beolvasott, de még fel nem dolgozott bitek
száma. 0 és 7 közötti.

$J
""
A Flate kitömörítés során már beolvasott, de még fel nem dolgozott bitek
értéke. 0 és 127 közötti.

$N
""
A Huffmann-fákat (és szabad csúcsokat) tartalmazó tömb. Formátumát már
korábban leírtam. A kitömörítés során háromféle típusú numerikus információ
kerül elő: karakterkódok, ismétlési offset-ek és ismétlési hosszak. Ezeket
az RFC 1951-ben leírt módon két Huffman-fa felhasználásával dekódolom,
mindkét fa az $N tömbben található.

$D
""
Az $N tömbben a szabad csúcsok láncolt listájának fejének indexe.

$G $B
"""""
Segédtömbök az m() függvényben.

$Z
""
A $Z tömb tárolja az m() függvényben építendő Huffman-fához az előírt
kódszóhosszakat.

$S
""
32768 byte hosszú string, körforgó buffer az ismétlődések kifejtéséhez.

$C
""
Az $S tömbbe legkorábban beírt karakter indexe, ez fog leghamarabb
ki-echo()-zódni. 

$T
""
Az $S tömb $T pozíciójára írható a következő kimeneti karakter.

$n
""
Az aktuális ZIP file neve.

Továbbfejlesztési lehetőségek
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. A ZIP kitömörítőn kívül a többi kódot tömörítve tárolni, és futtatási
   időben kibontani. Ezáltal több, mint 1000 byte-ot nyerhetnénk, ami
   felhasználható még néhány programozási nyelv szintaxis-színezésére, vagy
   szabványos HTML előállítására.

2. A Huffman-fa 3 -> 2 tömörítése.

3. Globális változók lokálissá tétele.

4. Hibajavítások.

__END__
Valid XHTML 1.0! Valid CSS!