CGIを使うメインとも言えるのが「掲示板」じゃないか?ということは、フォームから文字列を取得しなければいけない。たとえそれが日本語でも・・・。
ハッキリ言って仕組みはよくわからない。実際に使っているCGIから拝借しただけ・・・。
フォームの中身はブラウザから送信するときは、特に指定しない限り「application/x-www-form-urlencoded」で出力される(Content-Typeがこれになる)。urlに使えない文字(?)は「%hh」の形で変換(エンコード)された形式。これを元に戻す(デコード)のが次の命令。
s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
とりあえず、「pack("C", hex($1))」を実行した形式で置きかえるらしい。「$1」は正規表現で最初に出てきた「()」の中身を参照する。「hex」は文字列を16進数として解釈し、10進数に変換するようだ。どうやら「pack」は、10進数でしか機能しない関数のようだ。いや、嘘かも・・・。よくわからない。
あと、メソッド(送信形式?)にも、postやgetなどがあって、それによって受け取り方も変更しなくてはいけない。このサンプルでは「post」の形式しか使えない。
「CGI」に送信できるのはフォームの中身だけではなく、コマンド的(?)な文字列も渡すことができる。このコマンド(引数の方が相応しいかな?)を使えば、一つのCGIスクリプトで色々な処理をすることができる。このサンプルでは、引数無し(ts0016.cgi)だと「入力フォーム」を出力し、outputをつける(ts0016.cgi?output)とフォームの中身を出力するようにしている。この方法は「post」でしか使えないのか、「get」だと引数を渡すことができなかった。やり方がまずいのかもしれないけど・・・。もう少し調べてみたい。
この引数は「@ARGV」という変数(?)に渡される。2つ以上のコマンド(output+input+...のように書く)がある場合は、「@ARGV」という配列に順次渡される。先の例では「$ARGV[0]」が「output」、「$ARGV[1]」が「input」ということになる。扱い方は、通常の配列と同じようだ。つまり、コマンドが無い場合は「$#ARGV」は「-1」となる。
ところで、「ARGV」というのは、なんという単語の略なのか?覚えにくい・・・。
フォームの中身は、「post」の場合は「標準入力(STDIN)」に入力される。この入力を解釈するには以下のようにする。
read(STDIN, $query_string, $ENV{'CONTENT_LENGTH'});
これで、送信されたデータが「$query_string」の中に入力される。「$ENV{'CONTENT_LENGTH'}」は入力されたバイト数を返す環境変数のようだ。「STDIN」から「$ENV{'CONTENT_LENGTH'}」バイト分のデータを「$query_string」に代入する。とでも解釈すればいいのか・・・。
この内容は「名前1=中身1&名前2=中身2...」のようになっているため、まず「&」を区切りにして配列に入れる(配列 = split(/&/, 文字列))。次に、その文字列を「=」で区切って「名前」と「中身」に分離する((名前にする変数, 中身にする変数) = split(/=/, 分離させた文字列))。そして、「名前」を見出しにして「中身」を連想配列に入れる($連想配列名{名前} = 中身)。すると、変数の参照時に「名前」を用いる事ができる($連想配列名(見出し)で内容を参照する)ので、簡単にデータを扱う事が可能となる。詳しい事はソースを見ればわかる。と思う。この一連の流れは、連想配列への格納や参照の方法の勉強にもなる。
この他に、色々見なれない命令もあるが、そのうちわかるようになる・・・かもしれない。
ソースコード
#!/usr/bin/perl
# 初期設定
# require Jcode;
# $JcodeVer = "Jcode $Jcode::VERSION";
# *Jgetcode = \&Jcode::getcode;
# *Jconvert = \&Jcode::convert;
require "../../../cgi-bin/jcode.pl";
$JcodeVer = "jcode.pl $jcode::version";
*Jgetcode = \&jcode::getcode;
*Jconvert = sub { &jcode::to($_[1], $_[0], $_[2]); };
@G_styles = ('../../ipp.css','../test.css');
$G_title = 'テスト16';
$G_myCode = &Jgetcode('漢字');
$G_Code = 'jis';
$G_Charset = 'iso-2022-jp';
%G_form=();
$G_scrName = $ENV{'SCRIPT_NAME'};
if($G_scrName =~ /ts[0-9]{4}/){
$G_scrName = $&;
$G_linkFile = "../$G_scrName.htm";
}
{
&printHeader;
print "<div class=test>\n";
if($#ARGV == -1){
&printForm;
}elsif($ARGV[0] eq "output"){
&formRead;
&formWrite;
}
print "</div>\n";
&printFooter;
exit;
}
sub formWrite{
local($name, $value);
print "<table border=1>\n";
&Jprint("<tr><th>エレメントの名前<th>エレメントの中身</tr>\n");
while (($name, $value) = each(%G_form)){
&JconvPrint("<tr><td>$name<td>$value</tr>\n");
}
print "</table>\n";
}
sub formRead{
local($query_string, @elements, $elm, $name, $value, $code);
read(STDIN, $query_string, $ENV{'CONTENT_LENGTH'});
@elements = split(/&/, $query_string);
foreach $elm (@elements){
($name, $value) = split(/=/, $elm);
$value =~ s/\+/ /g; # =~ tr/+/ /; と書くのが普通なのか?
$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg;
$code = &Jgetcode($value);
if($code ne 'euc'){ $value = &Jconvert($value, 'euc', $code); } # とりあえずEUCに変換
$value =~ s/&/&/g; # メタ文字(?)を無効化
$value =~ s/</</g; # タグを無効化
$value =~ s/>/>/g; # タグを無効化
$value =~ s/\r\n?/\n/g; # 改行を統一
$value =~ s/\n/<br>/g; # 改行を<br>に変換
$G_form{$name} = $value;
}
}
sub printForm{
print "<form method=post action=\"$ENV{'SCRIPT_NAME'}?output\">\n";
print "<input type=text name=tx1 value=\"tx1\"><br>\n";
print "<input type=text name=tx2 value=\"tx2\"><br>\n";
&Jprint("<textarea name=tx3 cols=40 rows=4>tx3 日本語 英語 どちらでも\nタグなどは無効になります。</textarea><br>\n");
print "<input type=submit value=\"Submit!\">\n";
print "</form>\n";
}
sub printHeader{
if($G_Charset){
print "Content-type: text/html; charset=$G_Charset\n\n";
}else{
print "Content-type: text/html\n\n";
}
print '<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN">'."\n";
print "<html lang=ja>\n<head>\n";
if($G_Charset){ print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$G_Charset\">\n"; }
print '<meta http-equiv="Content-Script-Type" content="text/javascript">',"\n";
print '<meta http-equiv="Content-Style-Type" content="text/css">'."\n";
&Jprint ("<title>$G_title</title>\n");
foreach (@G_styles){ print "<link rel=\"stylesheet\" type=\"text/css\" href=\"$_\">\n"; }
print "</head>\n<body>\n";
print "<div class=head>\n";
&Jprint ("<h1>$G_title</h1><hr>\n");
&printlinks;
print "<hr></div>\n";
}
sub printFooter{
print "<div class=foot><hr>\n";
&printlinks;
print "<hr>\n";
&Jprint("漢字コード変換 : $JcodeVer<br>\n");
open(IN, '../../sig.txt');
print while (<IN>);
close(IN);
print "</div>\n";
print "</body></html>\n";
}
sub printlinks{
print "<a href=\"../../../index.htm\">Home</a>\n";
print "/\n<a href=\"../../\">Perl</a>\n";
print "/\n<a href=\"../\">TestCGI Index</a>\n";
if($G_linkFile){ &Jprint ("/\n<a href=\"$G_linkFile\">$G_titleの解説</a>\n"); }
}
sub Jprint{
if($G_Code eq $G_myCode){
foreach (@_){ print; }
}else{
foreach (@_){ print &Jconvert($_, $G_Code, $G_myCode); }
}
}
sub JconvPrint{
foreach (@_){ print &Jconvert($_, $G_Code, &Jgetcode($_)); }
}
