CGI（シージーアイ、''Common Gateway Interface''）とは、ウェブサーバが独立した外部プロセス（CGIプログラム）でウェブページを生成できるようにする仕組みです。CGIプログラムの記述にはシェルスクリプトでも）用いることができます。
ウェブサーバのApacheは、PHP・Perl・PythonやRubyのようなスクリプティング言語のコードだけでなく、C言語をコンパイルしたバイナリファイルも実行できます（ただし、バイナリなので、ホストアーキテクチャごとにコンパイルしなおす必要があり、取り回しが悪いので、例外的に処理速度が極端に必要な場合以外には、めったにC言語によるバイナリをCGIに使用することはありません）。
広い意味での『CGI』の「プログラミング言語の内容を実行する」との意味は、具体的にはウェブページでユーザからの入力に応答したり、動的な出力を行ったりするための機構などもCGIです。CGIの規格はhttp://hoohoo.ncsa.uiuc.edu/cgi/interface.html（インターネットアーカイブ： https://web.archive.org/web/20070809114039/hoohoo.ncsa.uiuc.edu/cgi/interface.html ）で定められています。ここで動的とはたとえば、ブラウザからリクエストを受け付けた日時をページとして表示させるものも動的なページの一つです。ウィキやブログなども動的なページに含まれます。これに対して静的とはあらかじめ用意してあるHTML等で記述されたドキュメントをリクエストへの応答時に変更を行わずに配信することを指しています。
PerlやPHPなど用いている言語が何かかは別として、現在インターネット上で大規模、あるいは著名なウェブサイトのほとんどは何らかの動的な仕組みを有していると考えられます。CGIの仕組みを理解することは大規模なデータをインターネット上で出版する技術的な背景を学ぶのと強い関係があると言えます。
動的なウェブアプリケーションの実装技術は、CGI以外にも mod_perl のようなApache向けのモジュールや FastCGI のような外部の常駐プロセスとの連携技術があります。
これらのCGI以外の方法が用いられるのは、CGIには「外部プロセスをリクエストがあるごとに起動しなければいけない」という欠点があり、プロセスの起動は毎秒数百から数千が限度で、この数は比較的小規模のウェブサイトでも容易に達する数です。このことからCGIでリクエスト毎にプロセスを起動するのではなく、動的コンテンツをモジュールや常駐プロセスに生成するのが現在の主流です。
このウィキブックスをサービスしているMediaWikiもPHPの常駐プロセスで実装されています。
CGIが行う動的な作用は主に以下の4要素によって成り立っています。
ウェブサーバで動的にコンテンツを生成する仕組みには他に、ウェブサーバのモジュール（mod_perlなど）やFastCGIがあります。
これらのCGIと大きな違いは、CGIの「リクエストごとに新しくプロセスを生成する」という負荷の大きな処理を、ウェブサーバプロセス内で実行したり、常駐するプロセスとウェブサーバの間での通信を行うことで避けている点です。
 の書換えの必要があります。
XAMPPであれば、場所は、\xampp\apache\conf の中に設定ファイル
があるので、探して httpd.conf のファイルの中身の下記のような部分を、下記のように書換えます。
多くのGNU/Linuxのディストリビューションでは、 /etc/httpd/conf に httpd.conf があります。
 などとし書込み保護した状態を維持してください。
過去の編集で、httpd.conf の所有者をログイン可能ユーザに変更することを指図する記述がありましたが、重大なセキュリティホールとなるので、もしその記載の通りに設定してしまった方がいたら、/etc/httpd.conf の所有者を root に戻し、所有者以外に書込みパーミッションを与えていないか確認ください。
設定の方法は2種類あります。
書き換え前
    AllowOverride none
    Require all denied
書き換え後
AllowOverride のあとを none から ALL に書き換えます。
Optionsのあとを「ExecCGI」に書き換えます。
    AllowOverride All
    Options ExecCGI
    Require all granted
ScriptAlias という行を、下記のようになるように、書き換えます。
ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
どちらの方法で編集するにしても、もしApacheをすでに立ち上げていたら、いったんApacheを終了してから、再度、立ち上げ直して下さい。
Apacheの起動時に設定ファイルを読み込む方式のようなので、立ち上げ直さないと、設定が反映されない場合があります。
Apacheを終了させるには、コマンド
 sudo service httpd stop
でApacheが終了します。
右上のXボタンを押してコマンドラインなどのウィンドウを閉じるだけでは、Apacheが終了しない場合が普通なので、終了させるためにはコマンド入力で確実に、いったんApacheを終了させてください。
Apacheを立ち上げるには、コマンド
 sudo service httpd start 
で立ち上がります。
今後の作業の暗黙の前提として、Apacheサーバーを立ち上げるのを忘れないように（よく忘れてエラーになります）。さっさと先にApacheサーバを立ち上げましょう。
さて、たとえば、下記のようなC言語ファイルをテキストファイル(『メモ帳』で良い)に書いて、コンパイル(gccでも良い)して、実行ファイル（windowsならexeファイル）にしましょう。
コード例
int main(void) {
  printf("Content-Type: text/html\r\n\r\n");
  int a = 3, b = 4;
  int c = a + b;
  printf("sum %d\n", c);
  return 0;
}
Content-Type: text/html
というのは、Apache側が解釈のために必要な情報であり、これから送られてくるprintf文の内容が、テキストファイルまたはHTMLファイルであることを宣言しているテキスト文です。「HTMLヘッダー」などと言われます、webでの情報のやりとりをする際の、送受信メッセージ文の一種です。
さて、Windows版ApacheであるXAMPPの場合、上記コード例をコンパイルして出来上がった実行ファイル(ファイル名を指定しなければWindows版gccなら「 a.exe 」という名前になります)を、
フォルダ
\xampp\cgi-bin
に入れればいいです。
GNU/Linux版の素のApacheの場合、フォルダ cgi-bin の場所は
 /var/www
ですので、そこにa.outなどの実行ファイルをいれればすみます。ですが、初期設定では、root所有になってますので、chownコマンドで所有者を変えてください。
 sudo chown ログインユーザ名 /var/www/cgi-bin
とにかく、cgi-bin フォルダにバイナリファイルを入れたら、のあと単にウェブ・ブラウザーで
 http://localhost/cgi-bin/a.exe
にアクセスすればいいです。
または実行ファイル名がa.exe以外の別のファイル名なら、
 http://localhost/cgi-bin/実行ファイル名
にアクセスすればいいです。
すると、
上記コードの場合
 sum 7
というふうに、webページでprintf文の内容が表示されます。(GNU/Linux版Apacheでも同様の結果です。Fedora 32 で2020年7月14日に確認。)
C言語バイナリだろうが、Apache側がHTMLファイルを解釈してくれるので、なので、下記のようにprintf文中にHTMLタグを書いてバイナリ化しておけば、自動的にApacheがうまく変換して、ブラウザにHTMLタグの指示通りに表示できるようにしてくれます。
コード例
int main(void) {
  printf("Content-Type: Text/html\r\n\r\n");
  int a = 3, b = 4;
  int c = a + b;
  printf("sum %d\n", c);
  return 0;
}
上記コードをコンパイルしたバイナリファイルをフォルダ cgi-bin に入れたあと、ウェブ・ブラウザーでアクセスすると、大きな文字で、
 sum 7
と表示されます。
(GNU/Linuxでも同様の結果です。Fedora 32 で2020年7月14日に確認。)
もし、興味あるのがCGIを使ったサーバ公開ではなく、CGIそのものの機能を作りたい場合、そのためのCGIの原理の知識は色々と考えられますが、OSのコマンドラインに搭載されている
が、原理的と思われる方法です。（なお、理解のために方法を紹介しているだけであり、通常のサーバ構築ではCGI機能自体の新規の実装は不要な作業です。すでにApacheなどの既存のサーバソフトにCGI機能が搭載されているからです。）
また、コマンドプロンプトに実行させたいコマンド列をテキストファイルに記述して繰り返し使うことが出来ます。
Windowsならバッチファイル、UnixまたはUnixに類したOSならばシェルスクリプトと呼ばれます。
本科目では、リダイレクトについて説明します。バッチファイルの解説は別の科目にゆだねます（たとえば『DOS入門』などを参照してください）。
リダイレクトについては、Windowsの場合、コマンドプロンプトで
 実行ファイル名 1> リダイレクト先のファイル名
と入力すれば、標準出力に出される文字列がそのまま、リダイレクト先のファイルに書き込まれて保存されます。
たとえば実行ファイル名「hello.exe」で、リダイレクト先ファイル名「ridtest.txt」なら
 hello.exe 1> ridtest.txt 
というコマンドになります。
間の演算子の機能は
1> : 標準出力のリダイレクト
2> : 標準エラー出力のリダイレクト
になります。
CGI用のウェブサーバーにおいて、CGIプログラムの出力を処理する方法は複数あります。一般的には、指定されたCGI用のフォルダ内にある実行ファイルをリダイレクトして実行することで、その出力をテキストファイルとして扱うことができます。ただし、この方法は現代の実装ではあまり使用されていません。
実際の現場では、より効率的な方法が一般的に採用されています。例えば、Apacheや他のウェブサーバーでは、CGIプログラムの出力を直接パイプラインに渡し、他のプログラムや処理と連携させることができます。この場合、リダイレクトや一時ファイルの作成を回避し、データの処理を効率的に行うことができます。
パイプラインを使用することで、CGIプログラムの出力を別のプログラムや処理に直接渡すことができます。これにより、リアルタイムのデータ処理や複雑なデータフローを実現することができます。また、パイプラインを使用することで、複数のプログラムを連鎖させてデータを処理することも可能です。
実行ファイルがカレント・ディレクトリにある場合、
ストリームを指定したリダイレクトのためのコマンドの書式は、
./実行ファイル名 1> リダイレクト先のファイル名
です。これで、リダイレクト先ファイルに、書き込まれます。
この書式は、sh ksh bash zsh  に共通ですが csh とは異なります。
たとえば実行ファイル名 hello で、リダイレクト先ファイル名が text.txt なら
./hello 2> text.txt 
というコマンドになります。
なお、ストリーム番号の意味は
になります。
Perl/CGI のページも見てください。
Perl/ライブラリ・モジュールとオブジェクト指向 のページもご覧ください。
テキストエディタ TeraPad(テラパッド)等のテキスト(拡張子 *.txt）を *.cgi に変えた物です。書かれている内容はtextなのでコード指定はテキストエディタのファイルオープンでUTF-8に変えてから日本語が使えます。変えないと文字化けします。
パソコンで初期の拡張子の表示を「表示する」にしないと、*.cgi.txt になってしまいます。名前付けは英字と数字しか使えません。Windowsの場合、コントロールパネルの検索で「拡張子」で設定してください。
動作が確認され ミスがないのが確認されたら、契約サーバーにアップロードをFFFTP等でおこないます。皆さんに楽しんでもらいましょう。属性(パーミッション)の変更もお忘れなく。
Perl/制御構造・Perl/リファレンス・Perl/はじめに もご覧ください。
とほほのWWW入門は、良い情報源になるかもしれません。
本書ではApache HTTP Serverを用いた例を示しますが、ほかにも多くのウェブサーバでCGIが利用可能です。
ファイル名は、以下指定なき物は「mihon.cgi」ディレクトリ(フォルダー)はサーバーの場合なんでもよいですが「test-cgi」が無難かも知れません。
ローカルサーバーの場合、アパッチの指定されたフォルダーの中htdocsやcgi-binに「test-cgi」が無難かも知れません。「test-cgi」はウインドーズの場合、プロパティの書き換えなどの指定か互換性の変更が確か必要だったと思います。
ローカルサーバーの呼び出し実行は「http://127.0.0.1/test-cgi/mihon.cgi」とか「http://127.0.0.1/cgi-bin/test-cgi/mihon.cgi」をアドレスとして呼び出してください。
127.0.0.1はIPv4においてlocalhostローカルホストに当たるアドレスです。
契約サーバーは場所指定があったり、説明書きを読まないと分りません。『public_html「test-cgi/mihon.cgi」』など。
アップしたら属性(パーミッション)を実行可能な700または755またはサーバー指定の値に変更します。
ローカルの場合32bitと64bitのバージョンがあるので注意してください。また、アパッチの場合、conf の httpd.conf を書き換え、追加など必要だったと思います。これはインストールされたスタート内のプログラムからも出来ると思います。
Statを押しても黒い箱が出てくる：ちょっと待ってください。Rrestatを押して再起動は出来ませんか？動いたをStopさせた後はRrestatで起動です。
Windowsでは通知領域「USB 抜差し等の▲の中」に入っています。
Perl http://www.activestate.com/ の中の アクティブパル http://www.activestate.com/activeperl/downloads 自分のパソコンを選んでね。トップページは見てください。
パールはPerl64の場合64を取ってPerlとして覚えて置いてください。パール → アパッチ の順でインストールしてください。
過去にウイルス対策ソフト「ノートン」において動作させられませんでしたが、現在は改善されているものと思われます。
Perlリファレンスなど公開されているリファレンス(レファレンス（reference）とも言う) を組み合わせて一つのプログラムとして組み上げます。
不勉強の為、ウインドウズしか持って居ないのでそれしか分りません。詳しくは加筆お願いします。
PHPやPerlとは関係なく、一般に Apache の起動の方法は、GNU/Linux(Fedora32)の場合、ターミナル画面で、コマンド
 sudo systemctl start httpd
です。(CentOS 7 以降はこうのようです。)
httpd とはlinuxの場合、Apache のことです。
なお、昔は
 sudo service httpd start
というコマンドのようでした。
Apache が正常に動いているか確かめるには、ブラウザを開き、アドレスバーに
http://localhost
と入力します。
thumb|Apache ロゴマークの羽
バージョンにもよりますが、Apacheのロゴマークの羽の絵のあるwebページが表示されていれば、たぶんインストール成功しているでしょう。
Apache を終了するには、GNU/Linuxならターミナル端末で
 systemctl stop httpd
で終了します。
昔は
 service httpd stop
で終了でした。
終了後に先程の localhost のリンク先に移動しても、何も読み込みできないハズです。(Apacheを終了したので、読み込みできないのが成功。)
そもそもアパッチをどうインストールすればいいかについては、たとえば『PHP/確実に動作させるまで』などに解説があります。(2020年4月21日の時点では、まだ Apache 専用のページはWikibooks日本語版にはありません）。
GNU/Linux の CentOS系の場合、フォルダ階層  var/www/html  に、目的のhtmlファイルを入れます。(なお、このようなフォルダ（そこにhtmlなどを入れるとサーバーが公開してくれる場所）のことをドキュメントルート DocumentRoot という。)
あらかじめ、目的のhtmlファイルを作っておく。
たとえば、serverTest.html というファイルが作ってあり、このhtmlファイルを公開したい場合、
まず、
 sudo cp serverTest.html /var/www/html
というコマンドになります。
SE Linux がオンだと設定が面倒なので、
 sudo setenforce 0
でSE Linuxをオフできます。
ウェブ・ブラウザーで  http://localhost/serverTest.html  にアクセスして、作成したhtmlどおりの内容が表示されれば、ここまでは成功。（外部公開するには、まだ作業が続く。）
ファイル名の部分（例では末尾の  serverTest.html ）は、作成したhtmlファイルのファイル名にします。
ウェブ・ブラウザーで確認し終わったら
 sudo setenforce 1
でSElinuxの設定をオンに戻す。
PerlでCGIプログラムをする場合、
perlだけでなく perl-CGI もインストールする必要があります。
GNU/Linux の Fedoraの場合、
 sudo dnf install perl perl-CGI
で両方とも入ります。sudo dnf install perl だけでは、perl-CGI がインストールされません。
Fedoraにインストールする場合、dnf コマンドでの perl-CGIの末尾3文字の「CGI」は大文字でなければなりません（でないとパッケージマネージャーが認識しません）。
下記のコードは、Perlによる単純なCGIプログラムの例です。CGIプログラムは、後述の設定をしたあとにウェブ・ブラウザーで閲覧して確認できます。（コマンド端末では確認できないか、著しく確認が困難。）
コード例
print "Content-Type: text/html\n\n";
print "Hello World!\n";
text/htmlのあとのエスケープシーケンスは必ず2つ \n\n としてください。もし1つだけだと、ブラウザで見てもエラーになり、「Hello World!」が表示されません。(もし \n が 1つだけだと「500  Internal Server Error」になります。)
解説
 #!/usr/bin/perl
というのは何かというと、これはshebangというOSの機能で、インタプリタに何を使うかを指定します。
書式はコメント文と同様に「#」から始まり形式的にはコメントですが、コメントではないので消さないでください。消すと動作しなくなります（例えば、bash のプロンプトから実行すると bash スクリプトとして perl スクリプトを実行してしまいます。おそらく bash の構文ではないのでエラーになります）。
PerlだけでなくUnix系のシェルスクリプトなど他のプログラム言語でも同様にshebangを記述する事があります。
 Content-Type: text/html
は、HTTPレスポンス・ヘッダーの一部で、ウェブ・ブラウザーなどユーザー・エージェントは、HTML本体とは別に、受信しようとする情報の種類などの打合わせのために HTTPヘッダーを送受信しあってます(Perlの場合は「CGIヘッダー」ともいい「HTTPレスポンス・ヘッダー」と区別していますが、Webサーバがヘッダー要素を追加する可能性がある為です)。 
そのHTTPレスポンス・ヘッダーで送受信しあう情報のひとつに「Content-Type: 」ヘッダーがあります。「Content-Type: text/html」というヘッダーによって「これからテキストの１つであるHTMLを送る」と相手先に伝えています。
コード例2
HTMLのソースコードを送りたい場合は、下記のように書きます。
print "Content-Type: text/html\n\n";
print "\n";
print "\n";
print "\n";
print "Example Web Page\n";
print "\n";
print "\n";
print "Hello, world!\n";
print "\n";
print "\n";
しかし実用的には、下記のようにプログラムを書いたほうがラクでしょう。
コード例3
use strict;
use warnings;
print <<"EOT";
Content-Type: text/html; charset=UTF-8
Example Web Page
Hello, world!
EOT
解説
use warnings; とは何かというと、これはプログラム中にエラーがあったら警告を出すという意味です。Perlはプログラム言語ですので、エラーも起こりえます。そのエラーの際に警告を出すという意味です。
ですが、これはコマンド端末で実行している場合のハナシです。
ウェブ・ブラウザーで見ている場合、そのような気のきいた警告はしてくれません。
また、use warnings; は警告をするだけですので、そのままプログラムを実行します。けっして、気をきかしてプログラム停止したりはしません。
use strict; は、プログラムの停止なども含めて、より厳格に判定および処置をします。
なので、上記プログラムから use warnings;  および  use strict; を除去しても、ウェブ・ブラウザー上で表示する事は可能です。
このファイルは、拡張子をかならず「.cgi」にしてください。（拡張子「.cgi」または「.pl」にしないと、今後の設定が面倒になります。）
このファイルを、フォルダ階層
 /var/www/cgi-bin
の中に配置します。
もし cgi-bin フォルダがまだ作れていない場合、perl-CGIがまだインストールされてないと思われるので、まずperl-CGIをインストールしてください。
所有者がrootになってるなどで、配置できないなら
 sudo chown ユーザー名 /var/www/cgi-bin
で所有者を変更できます。
冒頭の
 #!/usr/bin/perl
の部分は、環境によっては
 #!/usr/local/bin/perl
の場合もあります。
この部分 #!/usr/local/bin/perl は、perlのインタプリタを呼出してスクリプトを渡すための指示です。
これらperlインタプリタのバイナリの存在場所をさがすには、コマンド
 which perl
で探せます。
コマンド実行例
 $ which perl
 /usr/bin/perl
そして、制作したサンプルファイルは、アクセス権の設定で「プログラムとして実行可能」にチェックボックスを入れてください。右クリックで現れるダイアログから設定できると思います。
インタープリターへのパスがわからない場合は、あるいは色々な環境で動かすことが想定される場合（本書もそのケースです）
  #!/usr/bin/env perl
の様に POSIX でパスが決まっている env(1) を呼出し、（絶対パスでなく）コマンド名でインタープリターを指定します。
こうすると、env は環境変数PATHの中から順に インタープリター を探し、見つかったインタープリターにスクリプトを渡し起動します。
----
しかし、サーバがApacheの場合、まです、これだけでは動きません。
Apacheは初期設定では、cgiスクリプトを動かさない設定になっています。なので、まず、この初期設定を書き換える必要があります。
cgiスクリプトを動かせるように設定を変更するために、設定ファイルの httpd.conf というファイルを書き換えて、
 AddHandler cgi-script .cgi
という文章を追加する必要があります。
なお、通常のapacheでは、すでにコメントアウトされた状態で
 #AddHandler cgi-script .cgi
とあるので、単に冒頭のコメントアウト記号#をはずせばいいだけです。
この書き換えにより、拡張子 .cgi のあるファイルを、cgiスクリプトとして処理できるようになります。
なお、perlなどで使われる拡張子 「.pl」のファイルもCGIスクリプトとして実行したいなら、上記の AddHandler に
 #AddHandler cgi-script .cgi .pl
と「.pl」を追加するだけで済みます。
ただし、管理者が通常では root になっているので、そのままでは、書き換えできません。なのでGNU/Linuxの場合、コマンドで
 sudo chown ログインユーザ名 /etc/httpd/conf/httpd.conf
で、管理者を変えてから、管理者設定を書き換えることになります。
書き換えが終わったら、apache を立ち上げ直します。
そして、ウェブ・ブラウザーで
 http://localhost/cgi-bin/ファイル名.cgi
にアクセスしてください。
実行結果
ブラウザ画面上に
 Hello, world!
と表示されています。また、そのページのタイトルとして、タブ欄などに「Example Web Page」と書いてあります。
では、より実用的なプログラムを見ていきましょう。
下記のプログラムは、入力した文字列を、htmlのフォーム機能を使って別ファイル(例では catchtest.cgi ) に送るプログラムです。
コード例:
print <<"EOT";
Content-Type: text/html; charset=UTF-8
ユーザー名を登録: 
EOT
※ 『PHP/HTMLフォームからのデータ受け取り』と動作内容は同じです。
このように、パターンとして
print <<"EOT";
Content-Type: text/html; charset=UTF-8
EOT
というような書式で、単に  と  EOT  のあいだに、お決まりのパターンのコードを書くだけで、入出力機能のあるファームも簡単に作れます。
----
上記のコードの遷移先のページは、下記のようにつくります
コード例
print "Content-type: text/html\n\n";
$msg = ""
if ($ENV{'REQUEST_METHOD'} eq "POST") {
    read(STDIN, $msg, $ENV{'CONTENT_LENGTH'});
}
else {
    $msg = $ENV{'QUERY_STRING'};
}
print " $msg と入力されました。";
実行結果
たとえば ggggggg とブラウザに表示された入力ボックスにしてボタン「登録」を押すと、
 username=ggggggg と入力されました。
と表示されます。
解説
まず、遷移先のページにも、shebangやHTTPレスポンス・ヘッダーを忘れないようにしましょう（無いとエラーになります(Internal Server Error など) ）。
上記コードの if文 と else文 は、決まり文句です。 $msg以外はすべて、Perlでの決まり文句です。STDINは標準入力のことです。
Perlでは、formタグからのPOSTの受け取りは、標準入力 STDIN を通して受け取りが行われる仕様です。
環境変数として REQUEST_METHOD や CONTENT_LENGTH や QUERY_STRING という環境変数があらかじめ用意されています。なので上記コードでは、この変数はこのまま使う必要があります（勝手に名前を変えてはイケナイ）。
なお、環境変数を英語で environmental variable と言います。
 if ($ENV{'REQUEST_METHOD'} eq "POST") 
というのは、おおむね「もし環境変数 REQUEST_METHOD が POST なら」 のような意味です。
環境変数 REQUEST_METHOD には、フォームを呼び出した時のリクエストの結果がPOSTまたはGETのどちらかとして入っています。
eq 演算子は、「eqの左右の両辺をもし文字列とした場合に、両辺が等しいか？」を調べる演算子です。
いっぽう、== 演算子は、両辺を数値とした場合に等しいかを調べる演算子です。(C言語と違って、Perlでは変数の宣言時に型指定が無いので、条件分岐if文では、こういった演算子の区別が必要になります。)
なので、けっして eq演算子の部分を == 演算子に変えてはダメです。
なお、両辺が等しくない場合については、文字として評価する場合には ne 演算子、数値として評価する場合には != 演算子 です。
さて、表示結果の username というのは単に、勝手につけたオブジェクト名であり、呼び出し元のファイルのHTMLタグで勝手に命名したオブジェクト名ですので、もしそのオブジェクト名が変われば、表示結果の左辺のこの部分は名前になります。
結局
 オブジェクト名 = 受け取った内容
のように、オブジェクト名と一緒に、Perlでは POST で受け取った内容を管理する仕組みです。
より高度なCGIプログラムは次のようになります。
use strict;
use warnings;
use CGI;
my $q = CGI->new;
print $q->header( -charset => "UTF-8" );
print $q->start_html( -title => "Example Web Page" );
print $q->p("Hello, world!");
print $q->end_html;
上記の書き方は「信じられない植物ダウンロード」で検索して、参考に見てください。CGIのゲームです。
ちょっと古い見なれた構文 オリジナルです。printは一般的に使われています。
上は必ず一行目に書いてローカルホスト C:\Perl\bin\perl.exeを使うと言う定義です。コメントも書けません。
print "Content-type:text/html\n\n"; #\n改行がふたつ必要ですクッキーは上に書きます。
print <
てすと
test
EOF
print "動くかな。\n"; #print に続く物がプログラムです。ヘッダーとフッタに分割してサブルーチンとする事もできます。
print <<"EOF" ;
EOF
exit;
__END__
編集者の経験により意見が入るかも知れません。自論を押し付ける気はありません。
cgi-lib.pl(著作権ありと言うものがありデコードさせたり、ヘッダーやフッタを書き出すには便利ですが、融通が利かないという難点があります。
最近、CSSを使う事が多くなりましたが、対応状況が判りません。
JavaScriptもあったり、ヘッダーの可視性が不十分です。
[https://ja.wikipedia.org/wiki/Jcode.pl jcode.pl]（著作権あり）もよく見かけますが書いた言語と同じ言語が通常戻ってきます。メール用[https://ja.wikipedia.org/wiki/Sendmail Sendmail]の[https://ja.wikipedia.org/wiki/JIS%E6%BC%A2%E5%AD%97%E3%82%B3%E3%83%BC%E3%83%89 JISコード]に変換させるには非常に便利ですがcgiからメールを送信しない場合は内部で言語変換させないのならば、あまり必要と思いません。
では、デコードをどの様に組むか書いていきます。cgi-lib.pl（著作権あり）を使うと$in{'送られてきたデータ'}と返されますので$In{'送られてきたデータ'}と書き換えます。
上記はサーバーで動かす時はサーバーの仕様書を見て変えてください。
$this_cgi = "mihon.cgi";
$getin = 0;
$max_size = 100;
&decode
&header
&main
&footer
exit;
sub main{
	print 'あなたは ';
	print "$In{'kakikomi'}";
	print 'と書き込みしましたね。'; # 全角空白は文字化けの為 ''を使って囲む。
	print << "EOF" ;
EOF
}
sub header {
	print "Content-type:text/html\n\n";
	print <<"EOF" ;
見本1
EOF
}
sub decode {
	my ($query,$pair);
	if($ENV{'REQUEST_METHOD'} eq 'POST') {
		read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
	} else {
		$query = $ENV{'QUERY_STRING'};
		if ($query ne "" && $getin == 1){&err("GET");}
	}
	my ($saizu)=length $query;
	if ($saizu > $max_size){&err("エラー・サイズオーバー");}
	
	foreach $pair (split(/&/, $query)) {
		my ($key, $value) = split(/=/, $pair);
	
	# 文字のデコード
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
		$value =~ s/\0/0/g;
		$value =~ s//>/g;
		$value =~ s/\r\n//g; #追加
		$value =~ s/\r|\n//g;
		$value =~ tr/+/ /;
		$In{$key} = $value;
	}
}
sub footer{
	print "";
	print "フォーム要素名データ";
	foreach $key (keys %In) {
		print "$key$In{$key}\n";
	}
	print "";
	print <<"EOF" ;
EOF
	exit;
}
sub err{
	&header
	print 'エラー'."\n";
	print "$_[0]\n";
	&footer
	exit;
}
少し難しくなってきました。HTMLの知識、スタイルシートの組み込み、ジャバスクリプトの書き込みが追加になっています。
ロックファイル、フォルダの好ましくない点を上げると、あっちもこっちもロックに来てロックがフル稼働になってしまうことです。
つまり、ロックの分散化が必要になります。ファイルハンドルで別名を使うファイルによって付けてやれば、ファイルハンドルの衝突も起きないしファイルの衝突、待ち時間の軽減になると思います。
これを踏まえた上で組んで見ました。
ランダムもシードを与えなければタイムが自動的になります。
ご要望があればもっと詳しく書きますが、とりあえずこんな物かと書き加えて試してみるのを目的に組みました。
上記はサーバーで動かす時はサーバーの仕様書を見て変えてください。
$this_cgi = "mihon.cgi";
$getin = 0;
$max_size = 100;
$cntfile = './count.cgi';
unless(-e "$cntfile"){
	open (FOUT, "> $cntfile") or &err("エラー・ファイルが作れません。");
	close (FOUT);
	chmod 0600,$cntfile;
}
$mini_fig = 6;
$datafile = './kiroku.cgi';
unless(-e "$datafile"){
	open (FOUT, "> $datafile") or &err("エラー・ファイルが作れません。");
	close (FOUT);
	chmod 0600,$cntfile;
}
&decode
&header
&main
&footer
exit;
sub counter {
	local($count,$cntup);
	# カウントファイルを読みこみ
	open(CUNT,"< $cntfile") || &err("Open Error: cntfile","in");
	eval{flock(CUNT, 1);};
	$count = ;
	close(CUNT);
	local($local_time);
	local($cnt,$kiroku_day,$keika_day,$today,$yestaday) = split(/<>/, $count);
	$local_time = time + (9*60*60);#GMT+9:00補正
	if (!$kiroku_day){
		$kiroku_day = $local_time - ($local_time % (24*60*60));
	}
	if ($local_time - $kiroku_day > 24*60*60){
		$keika_day += int(($local_time - $kiroku_day)/(24*60*60));
		if ($local_time - $kiroku_day > 2*24*60*60){
			$yestaday = 0;
		}else{$yestaday = $today;}
		$kiroku_day = $local_time - ($local_time % (24*60*60));
		$today = 0;
	}
	$today++;
	if (!$keika_day){$keika_day = 0; }
	if (!$yestaday){$yestaday = 0; } 
	$cnt++;
	open(CUNT,"> $cntfile") || &err("Write Error: cntfile","in");
	eval{flock(CUNT, 2);};
	print CUNT "$cnt<>$kiroku_day<>$keika_day<>$today<>$yestaday<>\n";
	close(CUNT);
	# 桁数調整
	while(length($cnt) < $mini_fig) { $cnt = '0' . $cnt; }
	#時間の整形
	$date_sec = time;
	($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($date_sec);
	local @week = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
	local $m_week = $week[$wday];
	$date = sprintf("%04d/%02d/%02d(%s) %02d:%02d:%02d",$year+1900,$mon+1,$mday,$week[$wday],$hour,$min,$sec);
	print "\n";
	print "\n";
	print "$cnt\n";
	print "経過$keika_day\n";
	print "今日$today\n";
	print "昨日$yestaday\n";
	print "\n";
	print "\n";
}
sub asobkiroku {
	$detskazu = int(rand(10))+1;
	if(6 <= $detskazu){$asobimese = 'あなたの勝ち';}else{$asobimese = 'あなたの負け';}
	open(DATS,"< $datafile") || &err("Open Error: datafile","in");
	eval{flock(DATS, 1);};
	@datas = ;
	close(DATS);
	unshift @datas,"$detskazu<>$asobimese<>$In{'kakikomi'}<>$date<>\n";
	if(@datas > 10){$#datas = 9;}
	open(DATS,"> $datafile") || &err("Write Error: datafile","in");
	eval{flock(DATS, 2);};
	print DATS @datas;
	close(DATS);
	foreach (@datas){
		($b_detskazu,$b_asobimese,$b_kakikomi,$b_date) = split(/<>/);
		if($b_detskazu >=6){
			print "$b_detskazu $b_asobimese コメント:$b_kakikomi $b_date\n";
		}else{
			print "$b_detskazu $b_asobimese コメント:$b_kakikomi $b_date\n";
		}
	}
}
sub main{
	&counter
	print 'あなたは ';
	print "$In{'kakikomi'}";
	print 'と書き込みしましたね。'; # 全角空白は文字化けの為 ''を使って囲む。
	&asobkiroku
	print << "EOF" ;
EOF
}
sub header {
	print "Content-type:text/html\n\n";
	print <<"EOF" ;
見本2
EOF
}
sub decode {
	my ($query,$pair);
	if($ENV{'REQUEST_METHOD'} eq 'POST') {
		read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
	} else {
		$query = $ENV{'QUERY_STRING'};
		if ($query ne "" && $getin == 1){&err("GET");}
	}
	my ($saizu)=length $query;
	if ($saizu > $max_size){&err("エラー・サイズオーバー");}
	
	foreach $pair (split(/&/, $query)) {
		my ($key, $value) = split(/=/, $pair);
	
	# 文字のデコード
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
		$value =~ s/\0/0/g;
		$value =~ s//>/g;
		$value =~ s/\r\n//g; #追加
		$value =~ s/\r|\n//g;
		$value =~ tr/+/ /;
		$In{$key} = $value;
	}
}
sub footer{
	print "";
	print "フォーム要素名データ";
	foreach $key (keys %In) {
		print "$key$In{$key}\n";
	}
	print "";
	print <<"EOF" ;
EOF
	exit;
}
sub err{
	if($_[1] ne "in"){
		&header
	}
	print 'エラー'."\n";
	print "$_[0]\n";
	&footer
	exit;
}
下記のプログラムは一般的では無いかも知れませんがprint文で一気に書き出すのが楽ですし分りやすいです。 なので、モジュールは使用したくないのです。Perlでは、モジュール化して組み込む事も出来ます。
下記のプログラムは動的に動かすにはクッキーとソート「配列の中の第一変数を参照して」並べ替えを行っています。
分らない単語はここではプログラミングについての記述になるのでここでは触れません。リファレンスや事典を参照してください。
上記はサーバーで動かす時はサーバーの仕様書を見て変えてください。
$this_cgi = "mihon.cgi";
$getin = 0;
$max_size = 100;
$cntfile = './count.cgi';
unless(-e "$cntfile"){
	open (FOUT, "> $cntfile") or &err("エラー・ファイルが作れません。");
	close (FOUT);
	chmod 0600,$cntfile;
}
$mini_fig = 6;
$datafile = './kiroku.cgi';
unless(-e "$datafile"){
	open (FOUT, "> $datafile") or &err("エラー・ファイルが作れません。");
	close (FOUT);
	chmod 0600,$cntfile;
}
$COOKIE_NAME = 'mihon';
$COOKIE_LIFE = 7;
取り込みファイルの下準備通常は別ファイルとして作ります。
$require_txt = "errgo.cgi";
unless(-e "$require_txt"){
	open (FOUT, "> $require_txt") or &err("エラー・ファイルが作れません。");
	print FOUT  "sub err_go { &err(\"エラークエストがありました。\");}\n1;\n"; #ファイルの終わりには「1;」が必要。出来たファイルを見てください。
	close (FOUT);
	chmod 0600,$require_txt;
}
require './errgo.cgi';
&decode
&cookie_in;
if($In{'kakikomi'} eq "エラーゴー"){&err_go;} #エラーゴーと書かれた時エラーに行く。
&decode
&cookie_in;
&header
&main
&footer
exit;
sub counter {
	local($count,$cntup);
	# カウントファイルを読みこみ
	open(CUNT,"< $cntfile") || &err("Open Error: cntfile","in");
	eval{flock(CUNT, 1);};
	$count = ;
	close(CUNT);
	local($local_time);
	local($cnt,$kiroku_day,$keika_day,$today,$yestaday) = split(/<>/, $count);
	$local_time = time + (9*60*60);#GMT+9:00補正
	if (!$kiroku_day){
		$kiroku_day = $local_time - ($local_time % (24*60*60));
	}
	if ($local_time - $kiroku_day > 24*60*60){
		$keika_day += int(($local_time - $kiroku_day)/(24*60*60));
		if ($local_time - $kiroku_day > 2*24*60*60){
			$yestaday = 0;
		}else{$yestaday = $today;}
		$kiroku_day = $local_time - ($local_time % (24*60*60));
		$today = 0;
	}
	$today++;
	if (!$keika_day){$keika_day = 0; }
	if (!$yestaday){$yestaday = 0; } 
	$cnt++;
	open(CUNT,"> $cntfile") || &err("Write Error: cntfile","in");
	eval{flock(CUNT, 2);};
	print CUNT "$cnt<>$kiroku_day<>$keika_day<>$today<>$yestaday<>\n";
	close(CUNT);
	# 桁数調整
	while(length($cnt) < $mini_fig) { $cnt = '0' . $cnt; }
	&dates
	print qq|\n|;
	print qq|\n|;
	print qq|$cnt\n|;
	print qq|経過$keika_day\n|;
	print qq|今日$today\n|;
	print qq|昨日$yestaday\n|;
	print qq|\n|;
	print qq|\n|;
}
sub dates {
	#時間の整形
	$date_sec = time;
	($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime($date_sec);
	local @week = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
	local $m_week = $week[$wday];
	$date = sprintf("%04d/%02d/%02d(%s) %02d:%02d:%02d",$year+1900,$mon+1,$mday,$week[$wday],$hour,$min,$sec);
}
sub asobkiroku {
	$detskazu = int(rand(10))+1;
	if(6 <= $detskazu){$asobimese = 'あなたの勝ち';}else{$asobimese = 'あなたの負け';}
	open(DATS,"< $datafile") || &err("Open Error: datafile","in");
	eval{flock(DATS, 1);};
	@datas = ;
	close(DATS);
	unshift @datas,"$detskazu<>$asobimese<>$In{'kakikomi'}<>$date<>\n";
	if(@datas > 10){$#datas = 9;}
	open(DATS,"> $datafile") || &err("Write Error: datafile","in");
	eval{flock(DATS, 2);};
	print DATS @datas;
	close(DATS);
	foreach (@datas){
		($b_detskazu,$b_asobimese,$b_kakikomi,$b_date) = split(/<>/);
		if($b_detskazu >=6){
			print "$b_detskazu $b_asobimese コメント:$b_kakikomi $b_date\n";
		}else{
			print "$b_detskazu $b_asobimese コメント:$b_kakikomi $b_date\n";
		}
	}
	# 先頭の要素による並べ替え
	@keys1 = map {(split /<>/)[0]} @datas;
	@new_datas = @datas[sort {$keys1[$b] <=> $keys1[$a]} 0 .. $#keys1];
	print "\n";
	foreach (@new_datas){
		($b_detskazu,$b_asobimese,$b_kakikomi,$b_date) = split(/<>/);
		if($b_detskazu >=6){
			print "$b_detskazu $b_asobimese コメント:$b_kakikomi $b_date\n";
		}else{
			print "$b_detskazu $b_asobimese コメント:$b_kakikomi $b_date\n";
		}
	}
	print "クッキーは $COOKIE{'kakikomi'} と $COOKIE{'date'} が表\示されます。\n"; # 表は文字化けを起こすので\を入れます。
}
sub main{
	&counter
	print 'あなたは ';
	print "$In{'kakikomi'}";
	print 'と書き込みしましたね。'."\n"; # 全角空白は文字化けの為 ''を使って囲む。
	&asobkiroku
	print << "EOF" ;
EOF
}
sub set_cookie{
	if ($In{'kakikomi'}){	#書込の時限定。
			&dates # 日付と時間のサブルーチン
			$COOKIE{'kakikomi'} = $In{'kakikomi'};
			$COOKIE{'date'}  = $date;
	}
}
sub cookie_in{
	my ($pair, $cpair);
	
	foreach $pair (split(/;\s*/, $ENV{'HTTP_COOKIE'})) {
		my ($name, $value) = split(/=/, $pair);
		
		# 単一のクッキー値から%COOKIEにデコード
		if($name eq $COOKIE_NAME) {
			foreach $cpair (split(/&/, $value)) {
				my ($cname, $cvalue) = split(/#/, $cpair);
				
				$cvalue =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
				$COOKIE{$cname} = $cvalue;
			}
			last;
		}
	}
}
sub cooki_hakkou{
	&set_cookie; # クッキーのセット
	my	(@cpairs, $cname, $cvalue, $value);
	if ($In{'coodel'}){$COOKIE_LIFE = -1;} # クッキー消去
	# %COOKIEを単一のクッキー値にエンコード
	foreach $cname (keys %COOKIE) {
		$cvalue = $COOKIE{$cname};
		$cvalue =~ s/(\W)/sprintf("%%%02X", ord $1)/eg;
		push @cpairs, "$cname#$cvalue";
	}
	$value = join('&', @cpairs);
	
	# グリニッジ標準時の文字列
	my	@mon_str = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
	my	@wdy_str = qw(Sun Mon Tue Wed Thu Fri Sat);
	my	$life = $COOKIE_LIFE * 24 * 60 * 60;
	my	($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime(time + $life);
	my	$date = sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
			$wdy_str[$wday], $mday, $mon_str[$mon], $year + 1900, $hour, $min, $sec);
	return ("Set-Cookie: $COOKIE_NAME=$value; expires=$date\n");
}
sub header {
	($my_cookie) = &cooki_hakkou;
	print "$my_cookie";
	print "Content-type:text/html\n\n";
	print <<"EOF" ;
見本2
EOF
}
sub decode {
	my ($query,$pair);
	if($ENV{'REQUEST_METHOD'} eq 'POST') {
		read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
	} else {
		$query = $ENV{'QUERY_STRING'};
		if ($query ne "" && $getin == 1){&err("GET");}
	}
	my ($saizu)=length $query;
	if ($saizu > $max_size){&err("エラー・サイズオーバー");}
	
	foreach $pair (split(/&/, $query)) {
		my ($key, $value) = split(/=/, $pair);
	
	# 文字のデコード
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
		$value =~ s/\0/0/g;
		$value =~ s//>/g;
		$value =~ s/\r\n//g; #追加
		$value =~ s/\r|\n//g;
		$value =~ tr/+/ /;
		$In{$key} = $value;
	}
}
sub footer{
	print "";
	print "フォーム要素名データ";
	foreach $key (keys %In) {
		print "$key$In{$key}\n";
	}
	print "";
	my ($name, $value);
	print "";
	print "クッキー要素名データ";
	while (($name, $value) = each(%COOKIE)) {
		print "$name$value\n";#\\n
	}
	print "";
	print <<"EOF" ;
EOF
	exit;
}
sub err{
	if($_[1] ne "in"){
		&header
	}
	print 'エラー'."\n";
	print "$_[0]\n";
	&footer
	exit;
}
アクセスポイントの環境変数を用いてプログラムを守るものです。
設置は出来るだけ上の方に書いた方が良いと思います。
∈_atakka;
sub in_atakka {
	local($c_tim,@tem_atakku,$i,$ma_aru,@tem_atakku_new,$ma_addr,$ma_host,$ma_tim,$ma_kaisu,@new_atakku_new,$count11);
	my $get_host = $ENV{'REMOTE_HOST'};
	my $get_addr = $ENV{'REMOTE_ADDR'};
	if ($get_host eq "" || $get_host eq $get_addr) {
		$get_host = gethostbyaddr(pack("C4", split(/\./, $get_addr)), 2) || $get_addr;
	}
	$c_tim = time;
	if(!(-e "atakka.cgi")){
		open(AT,"> atakka.cgi") || &disp
		close(AT);
	}
	
	open(AT,"< atakka.cgi") || &disp
	eval{ flock (AT, 1); };
	@tem_atakku = ;
	close(AT);
	$i=0;
	$ma_aru =0;
	@tem_atakku_new = (@tem_atakku);
	foreach (@tem_atakku){
		($ma_addr,$ma_host,$ma_tim,$ma_kaisu) = split(/<>/);
		if($ma_addr eq $get_addr && $get_host eq $ma_host && $ma_kaisu > 5){
			if($ma_tim + 600 < $c_tim){$ma_kaisu = 0;}else{&disp}
		}
		if(!($ma_addr eq $get_addr && $get_host eq $ma_host) && $ma_kaisu > 5){ #5
			$i++;
			next;
		}
		if($get_addr eq $ma_addr && $get_host eq $ma_host && $c_tim < $ma_tim + 2){
			$ma_kaisu++;
			$tem_atakku_new[$i] = "$get_addr<>$get_host<>$c_tim<>$ma_kaisu<>\n";
			$ma_aru =1;
			last;
		}else{
			$ma_aru =0;
			$ma_kaisu = 0;
			unless($#tem_atakku_new < 0 && $ma_kaisu > 5){splice(@tem_atakku_new,$i,1);}
			last;
		}
		$i++;
	}
	foreach (@tem_atakku_new){
		($ma_addr,$ma_host,$ma_tim,$ma_kaisu) = split(/<>/);
		if($c_tim > $ma_tim + 600){next;} #経過済みのタイムアウト者を消す。10分
		if(@tem_atakku_new > 3 && $c_tim > $ma_tim + 3 && $ma_kaisu <= 2){ #30以上の参加者で3秒以上経過して2回以下なら消すtest 3
			next;
		}
		push @new_atakku_new,"$_";
	}
	@tem_atakku_new = (@new_atakku_new);
	if(!$ma_aru){
		if(@tem_atakku_new > 5){&disp("アクセスが多いのでお待ちください。");} #50以上の参加者の時は新規に入るのを待ってもらう。test 5
		push @tem_atakku_new,"$get_addr<>$get_host<>$c_tim<>1<>\n";
	}
	if(@tem_atakku_new == 0){&disp("exit");}
	open(AT,"> atakka.cgi") || &disp("Fail");
	eval{ flock (AT, 2); };
	$count11 = 0;
	foreach (@tem_atakku_new){
		if(m/$get_host+/){$count11 = 1;}
	}
	if(!$count11){close(AT);&disp("exit2");}
	print AT @tem_atakku_new;
	close(AT);
	if(-z "atakka.cgi"){&disp("Fail=0");}
}
sub disp{
	print "Content-type:text/html; charset=UTF-8\n\n";
print <<"EOF";
エラー
過負荷によるエラーが起こりました。
$_[0]
10分ほど経ったらもう一度試してみてください。
EOF
	exit;
}
	local(@tem_atakku_new,$prit_out,$ma_addr,$ma_host,$ma_tim,$ma_kaisu);
	open(AT,"< atakka.cgi") || &disp
	eval{ flock (AT, 1); };
	@tem_atakku_new = ;
	close(AT);
	foreach (@tem_atakku_new){
		($ma_addr,$ma_host,$ma_tim,$ma_kaisu) = split(/<>/);
		$prit_out .= "($ma_addr,$ma_host,$ma_tim,$ma_kaisu)\n";
	}
	print "Content-type:text/html; charset=UTF-8\n\n";
print <<"EOF";
アタックチェック
ファイル内容確認
$prit_out
EOF
	exit;
自分の今のＩＰを登録識別する事で不正アクセスをしにくくします。
ディレクトリと２つのプログラムによる共有データを使う。
main.cgi実行ファイル
フォルダーhost3を作ってください。
  host.cgi実行ファイル
  in_host.cgi 空ファイル
  host_koushin_ari.txt空ファイル
$this_cgi = "main.cgi";
$max_size = 500;
$getin =1;
$host_kyuka = 'yes';
$ohna_name = 'ウィキブックス';
$ohna_pas = '0000';
$aikotoba = 'wikibooks';
$host_cgi = "./host3/host.cgi";
$in_host = "./host3/in_host.cgi";
$koshin_fail = './host3/host_koushin_ari.txt';
$ip_kazu = 5;
&decode
if($In{'mode'} eq 'nyuryoku'){&nyuryoku}
if($In{'mode'} eq 'admin'){&admin}
&syoki
exit;
sub syoki {
	&acsesu
	print "Content-type:text/html\n\n";
	print <<"EOF" ;
メイン
$host_mes
EOF
	if ($ohna_name eq $In{'kanrisya_name'} && $ohna_pas && $ohna_pas eq $In{'kanrisya_pas'}){ #管理者のみ表示
		if(-e "$koshin_fail"){
			open (FIN, "$koshin_fail") or &err("エラー・ファイルが開けません..koshin_fail");
			eval{ flock (FIN, 1); };
			$tem_atakku = ;
			close(FIN);
			($henkou_time,$mese1,$mese2) = split(/ /, $tem_atakku);
			$now_time = time;
			if($now_time > $henkou_time + 2*24*60*60){$mese1 = "";$mese2 = "";}else{print "$mese1 $mese2\n";}
		}
	}else{
		print "管理者不一致\n";
	}
	print "";
	print "フォーム要素名データ";
	foreach $key (keys %In) {
		print "$key$In{$key}\n";
	}
	print "";
	print <<"EOF" ;
EOF
	exit;
}
sub acsesu {
	$host = $ENV{'REMOTE_HOST'};
	$addr = $ENV{'REMOTE_ADDR'};
	(@in_addr) = split(/\s/, $addr);
	$addr = $in_addr[0];
	$addr_in = $addr;
	if ($host eq "" || $host eq $addr) {
		$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2) || $addr;
	}
	if ($host eq "") {$host = $addr;}
	$host_in = $host;
	if ($host_kyuka eq 'yes' && (-e "$in_host")){
		if(!(-z "$in_host")){
			open(IN,"< $in_host") || &err2("Open Error : in_host");
			eval{ flock (IN, 1); };
			$kanri_ip = ;
			close(IN);
			chomp $kanri_ip;
			(@m_ip) = split(/<>/,$kanri_ip);
			$ok = 0;
			foreach (@m_ip){
				if($_ eq "$host_in $addr_in"){$ok = 1;last;}
			}
			if($_[0]){return ($ok);}
			if(!$ok){
				$ohna_pas = "";
				$host_mes = "ホスト一致がありません。";
			}else{
				$host_mes = "ホスト一致があります。";
			}
		}
	}
}
sub decode {
	my ($query,$pair);
	if($ENV{'REQUEST_METHOD'} eq 'POST') {
		read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
	} else {
		$query = $ENV{'QUERY_STRING'};
		if ($query ne "" && $getin == 1){&err("GET");}
	}
	my ($saizu)=length $query;
	if ($saizu > $max_size){&err("エラー・サイズオーバー");}
	
	foreach $pair (split(/&/, $query)) {
		my ($key, $value) = split(/=/, $pair);
	
	# 文字のデコード
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
		$value =~ s/\0/0/g;
		$value =~ s//>/g;
		$value =~ s/\r\n//g; #追加
		$value =~ s/\r|\n//g;
		$value =~ tr/+/ /;
		$In{$key} = $value;
	}
}
sub nyuryoku{
	print "Content-type:text/html\n\n";
	print <<"EOF" ;
管理室入り口
名前
パスワード
IP合言葉ホストが変更なしの場合書かなくてよい
EOF
	exit;
}
sub admin{
	($okok) = &acsesu(1);
	if(!($ohna_name eq $In{'kanrisya_name'} && $ohna_pas eq $In{'kanrisya_pas'})){return;}
	if($aikotoba eq $In{'aikotoba'} && !$okok){
		$host = $ENV{'REMOTE_HOST'};
		($addr) = split(/ /, $ENV{'REMOTE_ADDR'});
		if ($host eq "" || $host eq $addr) {
			$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2) || $addr;
		}
		if ($host eq "") { $host = $addr; }
		open (IN, "< $in_host") or &err("エラー・ファイルが開けません in_host");
		eval{ flock (IN, 1); };
		$f_host = ;
		@host_kiroku = ;
		close (IN);
		chomp $f_host;
		(@f_in_host) = split(/<>/, $f_host);
		$purasu = 0;
		foreach $deta(@f_in_host){
			if("$host $addr" eq $deta){$purasu = 1;}
			push @new_f_in_host,$deta;
		}
		if(!$purasu){unshift @new_f_in_host,"$host $addr";}
		if($#new_f_in_host > $ip_kazu){$#new_f_in_host = $ip_kazu;}
		$new_f_host = join ("<>",@new_f_in_host);
		$new_f_host .= "<>\n";
		($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time) ;	#一括取り入れ
		$year += 1900;	# $year = $year + 1900 と同じ
		++$mon ;
		@youbi=('日','月','火','水','木','金','土');
		$mond = sprintf("%02d",$mon);
		$mdayd = sprintf("%02d",$mday);
		$hourd = sprintf("%02d",$hour);
		$mind = sprintf("%02d",$min);
		$secd = sprintf("%02d",$sec);
		$jikan = "$year年$mond月$mdayd日$youbi[$wday]曜日$hourd時$mind分$secd秒";
		if($#host_kiroku >= 24){$#host_kiroku = 24;}
		unshift @host_kiroku,"$host $addr<>$jikan<>$host<>$ENV{'REMOTE_HOST'}<>$addr<>$ENV{'REMOTE_ADDR'}<>\n";
		open (OUT, "> $in_host") or &err("エラー・ファイルが開けません in_host");
		eval{ flock (OUT, 2); };
		print OUT $new_f_host;
		print OUT @host_kiroku ;
		close (OUT);
		$ima_time = time;
		open (FOUT, "> $koshin_fail") or &err("エラー・ファイルが開けません koshin_fail");
		eval{ flock (FOUT, 2); };
		print FOUT "$ima_time $host 許可ホストの変更がありました。";
		close (FOUT);
	}elsif(!$okok){return;}
	print "Content-type:text/html\n\n";
	print <<"EOF" ;
管理室
管理者の処理を行う場所です。
EOF
	exit;
}
sub err{
	if($_[1] ne "in"){
		print "Content-type:text/html\n\n";
		print <<"EOF" ;
エラー
EOF
	}
	print 'エラー'."\n";
	print "$_[0]\n";
	print <<"EOF" ;
EOF
	exit;
}
$this_cgi = 'host.cgi';
$ona_pas = 'wiki';
$kanre_name = 'ウィキブックス';
$ona_id = 'うぃきぺでぃあ';
$hozon_fail = 'in_host.cgi';
unless(-e $hozon_fail){
	open (FIN, "> $hozon_fail") or &err2("エラー・ファイルが開けません.0");
	close (FIN);
}
$koshin_fail = 'host_koushin_ari.txt';
$ip_kazu = 5;
$get_no = 1;
&loadformdata	#フォーム入力
&getoin
sub getoin{
	print "Content-type:text/html;\n\n";
	print <
許可ホスト変更
EOF
	$host = $ENV{'REMOTE_HOST'};
	($addr) = split(/ /, $ENV{'REMOTE_ADDR'});
	if ($host eq "" || $host eq $addr) {
		$host = gethostbyaddr(pack("C4", split(/\./, $addr)), 2) || $addr;
	}
	if ($host eq "") { $host = $addr; }
	$disp_ok = 0;
	if($FORM{'name'} eq $kanre_name && $FORM{'id'} eq $ona_id && $FORM{'pas'} eq $ona_pas && $FORM{'kanri'} eq $FORM{'kensa'}){
		open (IN, "< $hozon_fail") or die;
		eval{ flock (IN, 1); };
		$f_host = ;
		@host_kiroku = ;
		close (IN);
		chomp $f_host;
		(@f_in_host) = split(/<>/, $f_host);
		$purasu = 0;
		foreach $deta(@f_in_host){
			$i = 0;$loop = 0;
			foreach (0..$#f_in_host){
				$d_no = "d_no$i";
				if($FORM{$d_no} eq $deta){$loop = 1;}
				$i++;
			}
			if(!$loop){
				if("$FORM{'host_in'}" eq $deta){$purasu = 1;}
				push @new_f_in_host,$deta;
			}
		}
		if(!$purasu){unshift @new_f_in_host,"$FORM{'host_in'}";}
		if($#new_f_in_host > $ip_kazu){$#new_f_in_host = $ip_kazu;}
		$new_f_host = join ("<>",@new_f_in_host);
		$new_f_host .= "<>\n";
		&get_time;
		if($#host_kiroku >= 24){$#host_kiroku = 24;}
		unshift @host_kiroku,"$FORM{'host_in'}<>$jikan<>$host<>$ENV{'REMOTE_HOST'}<>$addr<>$ENV{'REMOTE_ADDR'}<>\n";
		$host_in = $FORM{'host_in'};
		open (OUT, "> $hozon_fail") or die;
		eval{ flock (OUT, 2); };
		print OUT $new_f_host;
		print OUT @host_kiroku ;
		close (OUT);
		$ima_time = time;
		open (FOUT, "> $koshin_fail") or die;
		eval{ flock (FOUT, 2); };
		print FOUT "$ima_time $host 許可ホストの変更がありました。";
		close (FOUT);
		$disp_ok = 1;
	}
	$kensa = sprintf("%04d",int(rand(10000)));
	print <許可ホスト変更
host = $host
addr = $ENV{'REMOTE_ADDR'}
名前：
ＩＤ：
パスワード：
確認： $kensaを左に入れてください
現在のホスト $host $addr
設定ホスト：
EOF
	if($disp_ok == 1){
		$i = 0;
		foreach (@new_f_in_host){
			print "$_\n";
			$i++;
		}
	}
	print <
EOF
	foreach (@host_kiroku){
		($host_disp0,$time_disp,$raitu_host,$addr_disp,$host_disp,$addr0_disp) = split(/<>/);
		chomp $addr0_disp;
		print "$host_disp0 , $time_disp : $raitu_host , $addr_disp , $host_disp , $addr0_disp\n";
	}
	print "\n";
	exit;
}
sub loadformdata {
	$max_size = 200;
	my ($query,$pair);
	if($ENV{'REQUEST_METHOD'} eq 'POST') {
		read(STDIN, $query, $ENV{'CONTENT_LENGTH'});
	} else {
		$query = $ENV{'QUERY_STRING'};
		if ($get_no ==1 && $query ne ""){&err2("エラー・GET 禁止");}
	}
	my ($saizu)=length $query;
	if ($saizu > $max_size){&err2("エラー・サイズオーバー");}
	foreach $pair (split(/&/, $query)) {
		my ($key, $value) = split(/=/, $pair);
		# 文字のデコード
		$value =~ tr/+/ /;
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/chr(hex($1))/eg;
		$value =~ s/\0/0/g;
		$value =~ s/&/&/g;
		if($value =~ m// ){&err2("禁止コード > があります。");}
		$value =~ s/"/"/g;
		$value =~ s/\x0D\x0A//g;
		$value =~ s/\r|\n//g; #追加
		$value =~ tr/\t//;
		$FORM{$key} = $value;
	}
	(@kennsa) = split(/ /, $FORM{'host_in'});
	if($kennsa[2]){&err2("コードの書き込み違反");}
}
sub get_time{
	($sec,$min,$hour,$mday,$mon,$year,$wday) = localtime(time) ;	#一括取り入れ
	$year += 1900;	# $year = $year + 1900 と同じ
	++$mon ;
	@youbi=('日','月','火','水','木','金','土');
	$mond = sprintf("%02d",$mon);
	$mdayd = sprintf("%02d",$mday);
	$hourd = sprintf("%02d",$hour);
	$mind = sprintf("%02d",$min);
	$secd = sprintf("%02d",$sec);
	$jikan = "$year年$mond月$mdayd日$youbi[$wday]曜日$hourd時$mind分$secd秒";
}
sub err2{
	print "Content-type:text/html;\n\n";
	print <
エラー
$_[0]
EOF
	exit;
}