練習

ここでは、簡単なCGIアプリケーションを例に取って、基本的なCGIのプログラミング方法の説明を行います。サンプルとするCGIアプリケーションは、ブラウザでRGBのパーセンテージで各色を指定して、16進数でその指定した色を表示すると同時に背景色として指定色を表示するものです。まず、その動きを見てみましょう。
ブラウザでrenshu.htmlというHTMLファイルを指定すると、次のように10%刻みで、Red、Green、Blueの3色を指定できる画面が表示されます。

[プログラムの実行]

初期入力画面

Redを100%にセットしてみます。

色指定後の画面

指定後、Submit Queryボタンを押してWWWサーバに送信します。送信データはCGIプログラムgetCol.cgiで処理され、その出力がWWWサーバから返されます。受け取った画面は次のようになります。

CGI処理後の表示画面

最初に表示を行うHTMLファイルrenshu.htmlは、以下のようになっています。
 
 
renshu.html
<HTML>
<HEAD>
</HEAD>
<BODY>
<FORM NAME="colorGenelator" ACTION="/cgi-bin/genCol.cgi">
<TABLE BORDER="1">
<TR ALIGN="center">
<TD BGCOLOR="#ff0000">Red</TD>
<TD BGCOLOR="#00ff00">Green</TD>
<TD BGCOLOR="#0000ff">Blue</TD>
</TR>
<TR>
<TD BGCOLOR="#ff0000">
<SELECT NAME="Red" SIZE="1">
<OPTION VALUE="0" selected>0 %</OPTION>
<OPTION VALUE="10">10 %</OPTION>
<OPTION VALUE="20">20 %</OPTION>
<OPTION VALUE="30">30 %</OPTION>
<OPTION VALUE="40">40 %</OPTION>
<OPTION VALUE="50">50 %</OPTION>
<OPTION VALUE="60">60 %</OPTION>
<OPTION VALUE="70">70 %</OPTION>
<OPTION VALUE="80">80 %</OPTION>
<OPTION VALUE="90">90 %</OPTION>
<OPTION VALUE="100">100 %</OPTION>
</SELECT>
</TD>

<TD BGCOLOR="#00ff00">
<SELECT NAME="Green" SIZE="1">
<OPTION VALUE="0" selected>0 %</OPTION>
<OPTION VALUE="10">10 %</OPTION>
<OPTION VALUE="20">20 %</OPTION>
<OPTION VALUE="30">30 %</OPTION>
<OPTION VALUE="40">40 %</OPTION>
<OPTION VALUE="50">50 %</OPTION>
<OPTION VALUE="60">60 %</OPTION>
<OPTION VALUE="70">70 %</OPTION>
<OPTION VALUE="80">80 %</OPTION>
<OPTION VALUE="90">90 %</OPTION>
<OPTION VALUE="100">100 %</OPTION>
</SELECT>
</TD>

<TD BGCOLOR="#0000ff">
<SELECT NAME="Blue" SIZE="1">
<OPTION VALUE="0" selected>0 %</OPTION>
<OPTION VALUE="10">10 %</OPTION>
<OPTION VALUE="20">20 %</OPTION>
<OPTION VALUE="30">30 %</OPTION>
<OPTION VALUE="40">40 %</OPTION>
<OPTION VALUE="50">50 %</OPTION>
<OPTION VALUE="60">60 %</OPTION>
<OPTION VALUE="70">70 %</OPTION>
<OPTION VALUE="80">80 %</OPTION>
<OPTION VALUE="90">90 %</OPTION>
<OPTION VALUE="100">100 %</OPTION>
</SELECT>
</TD>
</TR>
</TABLE>

<INPUT TYPE="submit">
<INPUT TYPE="reset">

</FORM>
</BODY>
</HTML>

このCGIスクリプトでポイントとなるのは、送信方法と起動するCGIプログラムを指定するFORMタグです。このrenshu.htmlでは、CGI<FORM NAME="colorGenelator" ACTION="/cgi-bin/genCol.cgi">でACTIONパラメータで起動するCGIプログラムを指定しています。送信方法METHODは特に指定してありませんから、デフォルトのGETとみなされます。
送信されるデータの記述方法は各タグによって異なりますが、renshu.htmlでは各SELECTタグのNAMEパラメータの値(それぞれRed,Green,Blue)と指定されたオプション(それぞれ100,0,0)のVALUEパラメータの値が送信されるデータとなっています。

<SELECT NAME="Red" SIZE="1">
<OPTION VALUE="100">100 %</OPTION>

<SELECT NAME="Green" SIZE="1">
<OPTION VALUE="0" selected>0 %</OPTION>

<SELECT NAME="Blue" SIZE="1">
<OPTION VALUE="0" selected>0 %</OPTION>

<INPUT TYPE="submit">タグで表示された[Submit Query]ボタンを押すと、送信方法としてGETが指定されているのでNAMEパラメータの値とVALUEパラメータの値は=で連結され、さらに=で連結された各々のNAMEパラメータとVALUEパラメータの対の値は&で連結され、URLエンコードされて一つの文字列としてWWWサーバに送信されます。URLエンコードの処理では、もしスペースがあれば'+'に変換されます。また、'&','=','%'があれば、特殊文字として%と2桁の16進数に変換されます。英数字、'*','-','@','_','.'以外の文字も同様に%と2桁の16進数に変換されます。この例はGETメソッドですから、送信データはLocation入力部に次のように表示されます。

http://localhost/cgi-bin/genCol.cgi?Red=100&Green=0&Blue=0

?の後のRed=0&Green=0&Blue=0の部分がCGIスクリプトに渡されるデータとなります。
WWWサーバでは、受け取った文字列を指定されたCGIプログラムgenCol.cgiに渡します。送信方法の指定である'GET'は環境変数REQUEST_METHODにセットされます。また、GETメソッドの場合、送信データは環境変数QUERY_STRINGにセットされます。起動されるCGIプログラムgenCol.cgiは、次の通りです。
 

genCol.cgi
#!/usr/bin/perl

*data = &parseInput($ENV{'REQUEST_METHOD'});

$red = 255 * ($data{'Red'}/100);
$green = 255 * ($data{'Green'}/100);
$blue = 255 * ($data{'Blue'}/100);

$bgcolor = sprintf("#%02x%02x%02x", $red, $green, $blue);
$fgcolor = sprintf("#%02x%02x%02x", $red^255, $green^255, $blue^255);

print "Content-type: text/html\n\n";
print "<HTML>\n";
print "<HEAD>\n";
print "<TITLE>Hello CGI</TITLE>\n";
print "</HEAD>\n";
print "<BODY BGCOLOR=\"$bgcolor\">\n";
print "<FONT COLOR=\"$fgcolor\">\n";
printf "RGB:%02x%02x%02x\n", $red, $green, $blue;
print "</FONT>\n";
print "</BODY>\n";
print "</HTML>\n";

exit(0);

#
# 入力データ解析ルーチン
#
sub parseInput
{
 local($method) = @_;
 local($query, @in, $key, $val);

 if ($method eq 'GET') {
  $query = $ENV{'QUERY_STRING'};
 }
 elsif ($method eq 'POST') {
  read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
 }

 local(@query) = split(/&/, $query);

 foreach (@query) {
  ($key, $val) = split(/=/);
  $key =~ s/%(..)/pack("c", hex($1))/ge;
  $val -~ s/%(..)/pack("c", hex($1))/ge;
  $in{$key} = $val;
 }

 return *in;
}
 

genCol.cgiでは、まず、環境変数REQUEST_METHODの値すなわち'GET'を引数として入力データ解析サブルーチンparseInputを呼び出し、送信データの解析を行っています。サブルーチンparseInputでは、送信データを分解・デコードし、それぞれNAMEパラメータの値とVALUEパラメータの値の対としてハッシュ%inに格納しています。データを格納した%inは、エイリアス(別名)の元になる型グラブ*inとして返されています。これで、inのエイリアスとなったハッシュ%dataにアクセスすれば、サブルーチンでセットした名前と値のデータにアクセスできるようになりました。

*data = &parseInput($ENV{'REQUEST_METHOD'});

ハッシュを使えば、$ハッシュ名{'キー'}とするだけで、キー(ここではNAMEパラメータの値)と対になった値(ここではVALUEパラメータの値)に簡単にアクセスできます。
例えば、255段階で色を表すように色の値を変換する次の式、

$red = 255 * ($data{'Red'}/100);

の$data{'Red'}は、その値100を示しているわけです。この式は 255 × ( 100 ÷ 100 ) となり、255が$redに代入されます。$data{'Green'}/100)は0、$data{'Blue'}も0ですから、$greenと$blueはそれぞれ0が代入されます。
さらに、これらをHTMLで解析できるように、sprintfを使って#nnnnnnという形式に再変換して、各変数に代入しています。

たくさんあるprint文は、デフォルトの出力先である標準出力に引数の文字列を出力します。CGIの標準出力は、通常ブラウザに表示するためのHTMLデータを構成し、WWWサーバを通じてブラウザに返され、表示されることになります。
後ほど詳細に説明しますが、参考までに入力データ解析サブルーチンparseInputをもう少し詳しく説明しておきましょう。サブルーチンで指定した引数は、局所化された特殊変数@_を経由してサブルーチンに渡されます。つまり、最初の文では変数$methodに引数の変数の値である'GET'がセットされます。

 local($method) = @_;
 local($query, @in, $key, $val);

$methodは、localで局所化されています。それ以外のlocal宣言された変数も、このサブルーチンの中だけで有効になるよう局所化されます。$methodの値は'GET'ですから、環境変数QUERY_STRINGの値(この例ではRed=100&Green=0&Blue=0)が$queryにセットされます。Perlでは、環境変数の値は、環境変数名をキーとしてハッシュ%ENVを参照することで得られます。この例では、$ENV{'QUERY_STRING'}や$ENV{'CONTENT_LENGTH'}がこれに当たります。

  $query = $ENV{'QUERY_STRING'};

次に、split関数を使い、'&'を区切り文字として送信データを分割しています。これで、NAMEパラメータの値=VALUEパラメータの値のリスト値(Red=100,Green=0,Blue=0)が得られ、それらは局所化された配列@queryにセットされます。

 local(@query) = split(/&/, $query);

この例に限ればデータは英数字だけなのでURLエンコードされた文字を元に戻すデコード必要ありませんが、この段階では、@queryの各要素である「NAMEパラメータの値=VALUEパラメータの値」の文字列はURLエンコードされた状態となっています。そこで、次の繰り返し処理を行うforeach文で、まず'='を区切り文字としてNAMEパラメータの値とVALUEパラメータの値を分割して$key, $valにセットし、次にpack関数を使ってデコード処理を行い、NAMEパラメータの値をキーとしてハッシュ%inにVALUEパラメータの値をセットしています。デコード処理はブラウザでデータを入力してもらうケースでは通常必要となる汎用的な処理なので、このスクリプトにも敢えて組み込んであります。

 foreach (@query) {
  ($key, $val) = split(/=/);
  $key =~ s/%(..)/pack("c", hex($1))/ge;
  $val -~ s/%(..)/pack("c", hex($1))/ge;
  $in{$key} = $val;
 }

そして、最後にinという名前を持つものすべてを表す型グロブinを戻り値として返しています。

 return *in;

いかがでしたか。
これから、さまざまなCGIアプリケーションが出てきますが、それらのCGIも基本的には上記のように動作します。処理が多くても、この基本が押さえられていれば理解しやすいと思います。では、具体的なCGIスクリプトのサンプルに進むことにしましょう。