WORLDNEWS
ISSUE 19
Perl CGI
Számos
lehetôségünk van, ha oldalunkhoz cgi-t szeretnénk gyártani. Rengetegféle
programnyelv között válogathatunk ezen cgi megírását illetôen,
azonban azt nyugodt szívvel kijelenthetjük, hogy ezen programnyelvek
között egy sincs annyira általánosan használva, mint a Perl. A
Perlben írt programunk, ellentétben az eddigi cikkeinkben szereplô
CGI programokkal nem bináris, hanem forrás állapotában kerül elhelyezésre
a serveren. A Perl ugyanis egy scripting nyelv, és interpreterrel
rendelkezik, a program tehát nincs elôre lefordítva. A felhasználó
persze ugyanúgy nem képes a forráshoz hozzáférni, mint a C programunk
esetében, azonban Perl interpreter a legtöbb szerverhez létezik,
így nem szükséges a programunk adott szerverre történô újrafordítása.
Mindkét
formának megvannak a maga elônyei, egyet azonban el kell ismernünk
(és mindenütt ezt is igyekeznek kihangsúlyozni), hogy szövegfeldolgozásban
a Perlnek kevés vetélytársa van. Azt már csak suttogva teszem
hozzá, hogy azért bizonyos tekintetben szorulna még azért fejlesztésre,
de azért a macera mennyisége miatt nem lehet komoly panaszunk.
Az
apropót egyébként az szolgáltatta ezen cikk megírására, hogy sokadik
programnyelvként a Perl is bekerült a vasárnapi óvodások szintjén
ismert programnyelveim sorába, olyannyira, hogy a WorldNews keresôje
Perlben lett megírva: hogy pedig ne vesszen kárba az ugyan nem
túl sok, talán nem is túl pontos ismeret, gondoltam jól megosztom
veletek.
Ha
még emlékszünk, mekkora fejfájásokat okozott annak idején C programunkban
már csak belegondolni, hogy csináljunk valamit, akkor felüdülésképpen
nézzünk is meg rögtön egy Perl forrást, amely mondjuk egy HTML-t
generál, egy helló felirattal a képernyôn.
#!
/usr/bin/perl
print <<HTML;
Content-type: text/html
<html>
<head />
<body>
helló
</body>
</html>
HTML
Ennyi
is lett volna. Ha jól megnézzük, a html forrás egy az egyben látható
a Perl forrásunkban, csupán körül lett rakva néhány utasítással.
Mielôtt továbblépnénk ezekre az utasításokra tegyünk egy kis kitérôt,
és vizsgáljuk meg magát a rövid kis html forrásunkat. Ami rögtön
szemet szúrhat benne, az a meglehetôsen furcsa <head />
tag. Miért írtam én ezt így vajon?
Készülôdöm
ugyanis az eljövendôre (jelenre?) és mivel elôbb-utóbb mindenkinek
meg kell majd szoknia, beszéljünk egy kicsit az XHTML-rôl.
Az
XHTML immár a HTML hivatalos - tehát standardizált - utódja, és
lassan minden és mindenki át fog térni ennek a használatára. Félreértések
elkerülése végett, ez így azért nem egy XHTML forrás még (hiányzik
belôle a kötelezô header rész, afféle doctype deklaráció, amivel
a WAP esetében már találkozhattunk. A forrás ugyan nem XHTML,
mégis megpróbál pár olyan szabályt betartani, amelyek majd az
XHTML-re lesznek jellemzôek.
Elôször
is, az XHTML sokkal kevésbé toleráns a HTML-nél. Nálam okosabb
emberek remélem tudják, hogy mit csinálnak, de akárhogy is, ez
így lesz sajnos. Ezért minden tag kisbetűs kell legyen, ugyanis
az XHTML betűméret érzékeny, nem lehetnek lezáratlan, vagy rossz
sorrendben lezárt tagok a forrásban. Minden tagot, még a soremelést
is zárnunk kell majd. A <br> tag tehát a jövôben így fog
majd kinézni: <br/>. Mivel azonban ez inkompatibilis a jelen
böngészôivel, ezért az XHTML kitalálói tettek egy kedvezményt,
és a következô formát ajánlják <br />. Ez ugyanis már átcsúszik
a mai böngészôkön, mégis megfelel az XHTML követelményeknek. Ez
lenne hát a magyarázata a <head /> tagnak, és ennyi lett
volna rövid kitérônk is az XHTML-rôl. Lehet, hogy egyszer késôbb
még visszatérünk rá, és bôvebben foglalkozunk vele.
Vegyük
azonban immár szemügyre a forrásunk Perl részét is. Legelsô sorunk
meglehetôsen furcsának tűnhet, nem szolgál azonban mást, mint
hogy UNIX környezetben a rendszer felismerje, hogy perl scriptrôl
van szó, és automatikusan képes legyen lefuttatni. (Itt az adott
serveren a perl interpreter elérési útját kell megadnunk, ez esetleg
más serveren máshol megtalálható. Mindenesetre ez tekinthetô a
szokásos helyének)
Ezt
követi egy meglehetôsen furcsa print utasítás. A módszer szintén
UNIX környezetbôl származik, és többsoros változódefiniáláshoz
használják. Számunkra a lényeg az, hogy a print <<HTML;
és a HTML rész közé (a HTML helyére tetszôleges változónév írható)
ugyanúgy írhatjuk HTML forrásunkat, mintha azt egy normális HTML
forrással tennénk, nem kell figyelemmel lennünk még az idézôjelekre
sem. A Perl ezt úgy fogja kiírni, ahogyan mi azt beírtuk.
A
többi meg ugye önmagáért beszél, miután a böngészôvel közöltük,
hogy milyen adatra számíthat (html szöveg), eljuttatjuk neki a
html forrást. (Az a két enter a content-type sor után
fontos!)
Meg
kell említenünk még egy dolgot, még mielôtt mindenki nagyon belevágna
a Perl forrása megírásába: míg a Windows nem éppen a legalkalmasabb
perl források írkálására, addig a Linux, Unix, Amiga és a többi
hasonló operációs rendszer tökéletes. (Windows alatt kénytelen
voltam UAE/Ced kombinációt használni, miután nem találtam épkézláb
szerkesztôt).
A
forrásaink ugyanis, ha Windows alatt szerkesztjük ôket kedélyesen
fognak lefagyni (kivéve a Windowsos perl fordítókat): a dolog
magyarázata pedig végtelenül egyszerű. Az Amiga (és a normális
operációs rendzserek ugye) mind ugyanúgy jelölik a sorok végét,
kivéve a gyevi bírót. Míg minden normális operációs rendszer esetében
egy karakter jelöli a sor végét, a Windowsban kettô: az unixos
perl fordítók pedig lelkiekben azért erre nincsenek felkészítve.
Haladjunk
kicsit tovább, írjunk ki most valami érdekesebbet, mondjuk a számokat
egytôl-tízig.
#!
/usr/bin/perl
print <<HTML;
Content-type: text/html
<html>
<head />
<body>
HTML
for ($i=1;$i<=10;$i++) {
print $i, "<br />";
}
print <<HTML;
</body>
</html>
HTML
Nem
sokban változtattuk meg forrásunkat, vegyük is sorjára mit csináltunk.
Elôször is mint látható a HTML kiírás egyetlen print utasítását
kettôbe vágtuk, hogy közé elhelyezhessük a ciklusunkat.
A
ciklus sokban emlékeztet egy C for utasításhoz, a különbség csak
annyi, hogy Perlben változóinkat egy $ jellel azonosítjuk (hasonlóra
C64-rôl emlékezhetünk, igaz ott ez csak a szöveges változókra
vonatkozott).
Mielôtt
innen továbblépnénk pár szót megint arról, hogyan lehet egy cgi-t
serveren elhelyezni. A cgi-t az arra kijelölt helyre kell elhelyeznünk
(általában cgi-bin alkönyvtár, vagy hasonló nevezetű), és mindenki
számára futtathatóvá kell tennünk (chmod 755). Ha külsô file-t
szeretnénk használni, akkor azt (amennyiben írni szeretnénk bele)
chmod 777 segítségével mindenki számára elérhetôvé kell tennünk
(írásra is), ellenben ha csak olvasni szeretnénk, akkor elegendô,
ha annak a felhasználónak olvasási joga van rá, aki a cgi-t létrehozta.
(Legalábbis az olyan webserverek esetében, ahol
a cgi a létrehozó felhasználó jogaival
rendelkezik, mint pl. az Apache.)
Vágjunk
is bele akkor az igazi fába, jöjjön a WorldNews keresôjének forrása:
#!
/usr/bin/perl
($cgi_bin, $cgi_script) = ($0 =~ m:(.*)[/\\](.*):);
$content_length = $ENV{CONTENT_LENGTH};
if ($content_length > 0) {
sysread(STDIN, $query, $content_length);
}
if (!defined($query) || $query eq '') {
#generate original html
print <<HTML;
Content-type: text/html
<html>
<head>
<title>Search Engine</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
</head>
<body bgcolor="#1C431C" leftmargin=0 topmargin=0
marginwidth=0 marginheight=0>
<table border="0" cellspacing="0" cellpadding="0"
align="center">
<tr>
<td><img src="../../sh/worldnews/images/searchtop.jpg"
width="640" height="67"></td>
</tr>
<tr bgcolor="#FFFFFF" valign="top">
<td>
<table border="0" cellspacing="0" cellpadding="8">
<tr>
<td><FORM METHOD="POST" ACTION="$cgi_script">
<b><font face="Arial, Helvetica, sans-serif"
size="3">STRING TO SEARCH FOR</font></b><input
type="text" name="field"><p >
<input type="submit">
</form>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><img src="../../sh/worldnews/images/searchbottom.jpg"
width="640" height="69"></td>
</tr>
</table>
</body>
</html>
HTML
}
else {
#display
print <<HTML;
Content-type: text/html
<html>
<head>
<title>Search Engine</title>
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
</head>
<body bgcolor="#1C431C" leftmargin=0 topmargin=0
marginwidth=0 marginheight=0>
<table border="0" cellspacing="0" cellpadding="0"
align="center">
<tr>
<td><img src="../../sh/worldnews/images/searchtop.jpg"
width="640" height="67"></td>
</tr>
<tr bgcolor="#FFFFFF" valign="top">
<td>
<table border="0" cellspacing="0" cellpadding="8">
<tr>
<td>
HTMLprint "<font face='Arial, Helvetica, sans-serif' size='3'><b>RESULTS:
<br>\n";
@fields = split('&', $query);
foreach (@fields)
{
/([^=]+)=(.*)/ && do {
local ($field, $value) = ($1, $2);
$query{$field} = &decode($value);
}
}
#print "square: $query{check}<br>\n";
#print "selector: $query{button}<br>\n";
print <<HTML;
<FORM METHOD="POST" ACTION="$cgi_script">
</b>Search string <b><input type="text"
name="field" size="20" value='
HTML
print &html($query{field}), "'></b></font><font
face='Arial, Helvetica, sans-serif' size='2'><input type='submit'
value='Search'></font><br>\n";
print "<font face='Arial, Helvetica, sans-serif' size='3'>";
$alma=&html($query{field});
#print $alma;
for ($i=1;$i<=8;$i++) {
if ($i==1) {
$currentdir="../../sh/worldnews/pages/200006/";
print "<hr><b>ISSUE 19</b><br><br>";
}
if ($i==2) {
$currentdir="../../sh/worldnews/pages/200002/";
print "<hr><b>ISSUE 18</b><br><br>";
}
if ($i==3) {
$currentdir="../../sh/worldnews/pages/9909/";
print "<hr><b>ISSUE 17</b><br><br>";
}
if ($i==4) {
$currentdir="../../sh/worldnews/pages/9906/";
print "<hr><b>ISSUE 16</b><br><br>";
}
if ($i==5) {
$currentdir="../../sh/worldnews/pages/9905/";
print "<hr><b>ISSUE 15</b><br><br>";
}
if ($i==6) {
$currentdir="../../sh/worldnews/pages/9903/";
print "<hr><b>ISSUE 14</b><br><br>";
}
if ($i==7) {
$currentdir="../../sh/worldnews/pages/9903e/";
print "<hr><b>ISSUE 14 ENGLISH</b><br><br>";
}
if ($i==8) {
$currentdir="../../sh/worldnews/pages/news/";
print "<hr><b>NEWS COLUMN</b><br><br>";
}
opendir(Dir,
$currentdir) || die;
$count=0;
$totalhit=0;
while ($file = readdir(Dir))
{
#print "$file\n";
$count=$count+1;
if ($count>2) {
open (FileBe, $currentdir.$file) || die;
$currenthit=0;
while ($sor = <FileBe>)
{
$sor =~ s/<([a-zA-Z0-9'"=\s\S]{1,})>//gi;
$sor =~ s/á/á/gi;
$sor =~ s/é/é/gi;
$sor =~ s/í/í/gi;
$sor =~ s/ó/ó/gi;
$sor =~ s/ö/ö/gi;
$sor =~ s/õ/ô/gi;
$sor =~ s/ú/ú/gi;
$sor =~ s/ü/ü/gi;
$sor =~ s/û/û/gi; if ($sor =~ /$alma/gi)
{
if ($currenthit==0) {
$currenthit=1;
$totalhit=$totalhit+1;
print "<a href='", $currentdir.$file, "'><b>",
$file , "</b></a><br>",$sor,"<br>\n";
}
}
#print $sor;
}
close(FileBe);
}
}
closedir(Dir);
if ($totalhit==0) {
print "NO MATCHING ARTICLE FOUND IN THIS ISSUE<br>";
}
else {
print "<br>", $totalhit, " occurences found";
}
}print <<HTML;
<hr>
<a href="http://abakusz.matav.hu/sh/worldnews">back
to the worldnews</a>
</font>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td><img src="../../sh/worldnews/images/searchbottom.jpg"
width="640" height="69"></td>
</tr>
</table>
</body>
</html>
HTML
}
sub decode {
local ($value) = @_;
$value =~ s/\+/ /g;
$value =~ s/%([0-9A-H]{2})/pack('C',hex($1))/eg;
return $value;
}
sub html {
local ($value) = @_;
$value =~ s/</</g;
$value =~ s/>/>/g;
return $value;
}
Nnno.
Ez lett volna. Fussunk át azokon a részeken, amelyek nem annyira
egyértelműek.
($cgi_bin,
$cgi_script) = ($0 =~ m:(.*)[/\\](.*):);
Ez
egy különösen érdekes sor, azt hiszem túloznék, ha azt mondanám
minden pontját értem: a lényeg, hogy az aktuális cgi elérési útját
két részre bontja, egy elérési útra (ezt helyezi el a $cgi_bin
változóba), és egy filenévre (ez kerül a $cgi_script változóba.)
Ez kicsit késôbb lesz fontos, a forrás <FORM METHOD="POST"
ACTION="$cgi_script"> sorában: ugyanis ha a cgi scriptünk
nem kap inputot a felhasználótól, akkor megjelenít egy formot,
amely form action paramétere saját magára mutat - ekkor kerül
felhasználásra a $cgi_script változó.
$content_length
= $ENV{CONTENT_LENGTH};
if ($content_length > 0) {
sysread(STDIN, $query, $content_length);
}
if (!defined($query) || $query eq '') {
Ebben
a kódrészletben beolvassuk a környezeti változóinkat (mivel nem
a query_string-en keresztül folyik a paraméterek átadása, hanem
az stdin-en keresztül, ezért elôször megszerezzük az ott várakozó
adat mennyiségét (content_length), majd a Perl sysread funkciójával
(amely bináris adatok olvasására szolgál, mivel azonban mi a késôbbiekben
szövegfile-okkal fogunk dolgozni, ezért lentebb már másik funkcióval
fogjuk a file-okat kezelni) beolvassuk az stdin-en található dolgokat.
Ha ott nincs semmi (ezesetben nem fog létrejönni a $query változónk,
vagy pedig ha létre is jön, üres változó lesz) akkor a formot
fogjuk megjeleníteni, amit kitöltve megint ehhez a cgi-hez térünk
majd vissza.
<FORM
METHOD="POST" ACTION="$cgi_script">
Mégpedig
az itt látható sorban, a $cgi_script változó segítségével. Amennyiben
pedig mégis van valami a $query változóban, akkor örömködünk,
és teljesen más fordulatot vesz a történetünk.
@fields
= split('&', $query);
foreach (@fields)
{
/([^=]+)=(.*)/ && do {
local ($field, $value) = ($1, $2);
$query{$field} = &decode($value);
}
}
Nnno,
ez látszólag már nem olyan egyszerű falat, mint az eddigiek. Igazság
szerint nem saját alkotásom ez a rövid kis programrészlet, mindenesetre
a feladatát tökéletesen ellátja. A célja az, hogy a $query-ben,
egyelôre még ömlesztve található inputot (azt, hogy ez hogyan
néz ki, azt a régebbi CGI cikkekben kiveséztük már, most legyen
elég annyi, hogy az egyes elemek & jellel vannak benne elválasztva,
elem=érték formátumban, kódolva.) a split parancs segítségével
szétbontson, majd végül egy asszociatív tömbbe (jó név, ugye?)
tároljon. Asszociatív tömb alatt olyan tömböt értünk, ahol ha
pl. Péter 23 éves akkor hivatkozhatunk a tömb péterre vonatkozó
adatára a következôképpen: $tomb(peter). Ekkor ennek az értéke
23 lesz. A tombunk a $query lesz, a hivatkozasunk az elem neve,
és a tömbelem értéke az adott elem értéke. Menet közben meghívásra
kerül a decode szubrutin is, amit amúgy a forrás végén találunk:
anélkül, hogy bôvebben belemásznánk, annak funkciója, hogy a %
jel és egy két számjegyű hexadecimális érték formában elkódolt
speciális karaktereket (ékezetek, néhány írásjel, etc.) visszaalakítsa
eredeti formájára. Amint az megfigyelhetô, a szubrutinok hívása
a &cimke formában történik.
opendir(Dir,
$currentdir) || die;
Itt
már a directoryt olvassuk, hogy megkapjuk a file-ok listáját,
amelyekben keresni fogunk. (az aktuális file-t a readdir(Dir)
funkciótól kapjuk meg). Ezután a
open
(FileBe, $currentdir.$file) || die;
sorral
meg is nyitjuk rögtön. Ezután a tartalmát soronként beolvassuk,
majd a Perl erre szolgáló kifejezéseivel (regular expressions)
a szövegsort cseréljük és hasonlítjuk mindenfélével. Igazság szerint
itt ismertetnem kellene a reguláris kifejezésket: azonban ez azt
hiszem jóval meghaladná már a WorldNews kereteit. (Ismertetni
kellene ehhez rengeteg mindent.) Legyen elég róluk,
hogy / jelek között kell lenniük, és megadott
minta szövegben való keresésére, illetve
megadott minta alapján a szövegben való lecserélésre
szolgálnak. Jelen esetünkben arra használjuk
õket, hogy a html sorokat normál szöveggé
konvertáljuk, végül pedig az így kapott
sorban rákeressünk a felhasználó által
megadott szóra/szavakra. A reguláris kifejezések
tetszõleges komplexitásúak lehetnek, de egy
táblázattal a kézben, amely az összes
reguláris kifejezést, valamint a keresés/lecserélés
lehetséges paramétereit tartalmazza viszonylagosan
rövid idõn belül ki lehet kombinálni egy-egy
keresési feltételrõl, hogy mit is akar csinálni.
(És ugyanígy visszafelé: egy ilyen táblázattal
a kézben gyártani is lehet õket. Ilyen táblázatokat
leginkább Perllel foglalkozó könyvekben találhatunk,
bár nem kizárt, hogy a neten is rábukkanhatunk
hasonlókra.) Ezek bôvebb megismeréséhez tehát tessék
egy Perl dokumentációt, vagy könyvet beszerezni.
A
forrás többi része szerintem némi tanulmányozás után könnyen megérthetô:
maga a Perl (szerintem) könnyen tanulható, mert nagyon sok más
nyelvre hasonlít, és sok helyrôl találhatunk benne átvett elemeket.
Angolul értôknek a net tele van ismertetôkkel, leírásokkal és
példaprogramokkal, de az angolul nem beszélôk is rendelhetnek
a Perl programozásával foglalkozó könyvet pl. a www.konyvesbolt.hu
címen.
Amiért
a Perl annyira elterjedt annak legfôbb oka az, hogy a programok
(egy része) sokkal gyorsabban és egyszerűbben megírható bennük,
szövegek feldolgozása is nagyon hatékonyan megy - ezek pedig olyan
elônyök, amelyeket nem feltétlenül kompenzálnak a C elônyei, ha
éppen ezekre lenne szükségünk.
Nem
utolsósorban néhány állat szolgáltatónál pedig csak a Perl cgi-k
engedélyezettek, azokból is csak a standard utasításkészletűek.
(Nem célozgatni akarok, de az Elenderrôl beszélek.) Persze, vannak
olyan szolgáltatók is, ahol csak az asp működik (NT server rulez(?)),
de azt hiszem annak még nem jött el a napja, hogy én a WorldNewsen
microsoft asp-t istenítsek... (pedig már abban is muszáj volt
írnom néhány programot, úgyhogy gyakorlati akadálya nem lenne
a dolognak).
Amit
ettôl a cikktôl remélek, hogy kevésbé rettenti majd el az embereket
a cgi írástól, mint a C nyelv, és egyre több és egyre komplexebb
hátterű weboldalunk lesz majd, nekünk Amigásoknak...
Emeric
SH