THESE PAGES ARE FREE OF

JAVA
FRAMES

STORED ON A LINUX SERVER, AND RUNNING SPACEHAWKS' OWN LINUX BASED VOTING AND MESSAGE BOARD CGI.

NO MICROSOFT RELATED PROGRAMS WERE USED TO CREATE THIS SITE.

VOYAGER
IBROWSE
AWEB
 
MSIE
 

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/&aacute;/á/gi;
$sor =~ s/&eacute;/é/gi;
$sor =~ s/&iacute;/í/gi;
$sor =~ s/&oacute;/ó/gi;
$sor =~ s/&ouml;/ö/gi;
$sor =~ s/&otilde;/ô/gi;
$sor =~ s/&uacute;/ú/gi;
$sor =~ s/&uuml;/ü/gi;
$sor =~ s/&ucirc;/û/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/</&lt;/g;
$value =~ s/>/&gt;/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

 

 

 

 
 
 
GLOBAL LINKS DOWNLOAD AREA MESSAGE BOARD