【 CGI用スケルトン 】

1. cgi.h
2. 実装
3. CGI 変数
4. マクロ
5. デコード
6. 対象データの取り出し

  • cgi 用のプログラミングをする為のヘッダファイルです。cgi-lib.pl のようなものです
    ( Visual Studio VC++6.0 を使用しています )
  • // **********************************************************
    // CGI 用ヘッダ
    // **********************************************************
    #include <windows.h>
    #include <stdio.h>
    #include <malloc.h>
    #include <stdlib.h>
     
    // 関数宣言
    void SetEnvValue( void );
    void DataDecode( char *buff );
    void PrepareVar( char *buff );
    void cgihead( void );
    void cgimain( void );
     
    // マクロ
    #define print(a) printf("%s\n",a);
    #define	CGIVAR(TARGET_VAR) TARGET_VAR = getenv(#TARGET_VAR); \
    if ( TARGET_VAR == NULL ) { \
    	TARGET_VAR = NULL_STRING; \
    } \
    printf( "<!--" #TARGET_VAR "=%s-->\n", TARGET_VAR );
     
    // CGI 変数
    char *REQUEST_METHOD;
    char *CONTENT_LENGTH;
    char *QUERY_STRING;
    char *SCRIPT_NAME;
    char *NULL_STRING = "";
     
    // GET 用バッファ
    char *getbuffer = NULL;
    // POST 用バッファ
    char *postbuffer = NULL;
     
    // **********************************************************
    // メイン
    // **********************************************************
    int main(int argc, char* argv[])
    {
     
    	// デフォルト HTTP ヘッダ
    	printf( "%s\n", "Content-Type: text/html; Charset=Shift_JIS" );
    	// 追加ヘッダ用ルーチン
    	cgihead();
    	// ヘッダ終了
    	printf( "\n" );
     
    	// CGI 変数作成
    	SetEnvValue( );
     
    	// GET 用バッファ領域確保
    	getbuffer = (char *)malloc( lstrlen(QUERY_STRING) + 10 );
    	if ( getbuffer == NULL ) {
    		printf( "%s<BR>\n", "メモリを確保できませんでした" );
    		return 0;
    	}
    	lstrcpy( getbuffer, QUERY_STRING );
    	DataDecode( getbuffer );
    	printf( "<!--QUERY_STRING=%s-->\n", getbuffer );
     
    	// POST 処理
    	if ( strcmp( REQUEST_METHOD, "POST" ) == 0 ) {
     
    		// POST 用バッファ領域確保
    		postbuffer = (char *)malloc( atoi( CONTENT_LENGTH )+10 );
    		if ( postbuffer == NULL ) {
    			printf( "%s<BR>\n", "メモリを確保できませんでした" );
    			return 0;
    		}
     
    		// POST データ取得
    		int ret,pos;
     
    		pos = 0;
    		postbuffer[0] = 0x00;
    		while( !ferror(stdin) ) {
    			ret = fread( postbuffer+pos, 1, atoi( CONTENT_LENGTH ), stdin );
    			if ( ret == 0 ) {
    				break;
    			}
    			postbuffer[pos+ret] = 0x00;
    			pos += ret;
    			if ( pos >= atoi( CONTENT_LENGTH ) ) {
    				break;
    			}
    		}
     
    		// コメントとして変換前の出力
    		printf( "<!--%s-->\n", postbuffer );
    		// %xx と + のデコード
    		DataDecode( postbuffer );
    		// コメントとして変換後の出力
    		printf( "<!--%s-->\n", postbuffer );
     
    		// データ取り出し準備
    		PrepareVar( postbuffer );
    	}
     
    	// データ取り出し準備
    	PrepareVar( getbuffer );
     
    	// ユーザ CGI メイン
    	cgimain();
     
    	// バッファ解放
    	if ( postbuffer != NULL ) {
    		free( postbuffer );
    	}
    	free( getbuffer );
     
    	return 0;
    }
     
    // **********************************************************
    // CGI 変数作成 ( ※適宜追加要 )
    // **********************************************************
    void SetEnvValue( void )
    {
    	CGIVAR(REQUEST_METHOD)
    	CGIVAR(CONTENT_LENGTH)
    	CGIVAR(SCRIPT_NAME)
     
    	CGIVAR(QUERY_STRING)
    }
     
    // **********************************************************
    // %XX と + デコードと 0D0A -> 0A 変換
    // **********************************************************
    void DataDecode( char *buff )
    {
    	int i,pos,len;
    	unsigned char c[4];
     
    	len = lstrlen( buff );
    	if ( len == 0 ) {
    		return;
    	}
    	char *TextData = new char[len+100];
     
    	pos = 0;
    	for( i = 0; i < len; i++ ) {
    		switch( buff[i] ) {
    			case '%':
    				c[0] = buff[i+1];
    				c[1] = buff[i+2];
    				c[2] = 0x00;
    				_strupr( (char *)c );
    				if ( strcmp( (char *)c, "0D" ) == 0 ) {
    					i = i + 2;
    					break;
    				}
     
    				c[0] -= 0x30;
    				if ( c[0] >= 10 ) {
    					c[0] -= 7;
    				}
    				c[1] -= 0x30;
    				if ( c[1] >= 10 ) {
    					c[1] -= 7;
    				}
    				TextData[pos] = c[0] * 16 + c[1];
    				i = i + 2;
    				pos++;
    				break;
    			case '+':
    				TextData[pos] = 0x20;
    				pos++;
    				break;
    			default:
    				TextData[pos] = buff[i];
    				pos++;
    				break;
    		}
    	}
    	TextData[pos] = 0x00;
    	lstrcpy( buff, TextData );
     
    	delete [] TextData;
     
    }
     
    // **********************************************************
    // データ取り出し用の準備
    // **********************************************************
    void PrepareVar( char *buff )
    {
    	int i,len;
    	len = lstrlen( buff );
     
    	for( i = 0; i < len; i++ ) {
    		switch( buff[i] ) {
    			case '=':
    				buff[i] = 0x00;
    				break;
    			case '&':
    				buff[i] = 0x00;
    				break;
    		}
    	}
     
    }
     
    // **********************************************************
    // 対象データ取り出し
    // **********************************************************
    char *GetValueAddress( char *buff, int len, char *name )
    {
    	int i;
     
    	for( i = 0; i < len; i++ ) {
    		switch( buff[i] ) {
    			case 0x00:
    				break;
    			default:
    				if ( lstrcmp( buff+i, name ) == 0 ) {
    					while( buff[i] != 0x00 ) {
    						i++;
    					}
    					return( buff+i+1 );
    				}
    				else {
    					while( buff[i] != 0x00 ) {
    						i++;
    					}
    				}
    				break;
    		}
    	}
     
    	return NULL_STRING;
     
    }
     
    // **********************************************************
    // GET 取り出し
    // **********************************************************
    char *GET( char *name )
    {
    	return GetValueAddress( getbuffer, lstrlen( QUERY_STRING ), name );
    }
     
    // **********************************************************
    // POST 取り出し
    // **********************************************************
    char *POST( char *name )
    {
    	return GetValueAddress( postbuffer, atoi( CONTENT_LENGTH ), name );
    }
    


    // cgi.cpp : コンソール アプリケーション用のエントリ ポイントの定義
    //
     
    #include "stdafx.h"
    #include "cgi.h"
     
    // **********************************************************
    // HTTP ヘッダ出力用 ( print マクロを使用するように )
    // **********************************************************
    void cgihead( void )
    {
     
    }
     
    // **********************************************************
    // CGI メインルーチン
    // **********************************************************
    void cgimain( void )
    {
     
    	print( "<HTML>" )
    	print( "<FORM method=post>" )
     
    	printf( "<INPUT type=text name=data1 value='%s'><br>", POST("data1") );
    	printf( "<INPUT type=text name=data2 value='%s'><br>", POST("data2") );
    	printf( "<TEXTAREA rows=10 name=area>%s</TEXTAREA><br>", POST("area") );
    	print( "<INPUT type=submit name=send value='送信'><br>" )
     
    	print( "</FORM>" )
    	print( "</HTML>" )
     
    }
    

    環境テーブル
  • CGI 変数は、getenv 関数で 環境テーブルに存在する文字列のポインタを
    使用します

  • 環境テーブルはプログラム実行中存在するものなので、アドレスを保存し
    ておいて使用します
  •  
    char *REQUEST_METHOD;
    char *CONTENT_LENGTH;
    char *QUERY_STRING;
    char *SCRIPT_NAME;
    


  • 必要に応じて変数を追加すれば良いと思います

  • その場合、変数を追加して後述のマクロで登録します
  • print マクロ
  • このマクロは、一つの文字列を改行付きで出力するものです

  • マクロとしては最も簡単なサンプルとなっています
  •  
    #define print(a) printf("%s\n",a);
    


    CGIVAR マクロ
  • このマクロは、複数行の定義サンプルです

  • さらに、マクロ内の "" 内にマクロの引数を展開する方法も示しています
  •  
    #define CGIVAR(TARGET_VAR) TARGET_VAR = getenv(#TARGET_VAR); \
    if ( TARGET_VAR == NULL ) { \
    	TARGET_VAR = NULL_STRING; \
    } \
    printf( "<!--" #TARGET_VAR "=%s-->\n", TARGET_VAR );
    


  • #引数 は、マクロの引数を " で挟んだ結果を展開します

  • さらに、#引数 を 文字列で挟むと、連結されて反映されます
  • 16進数文字列を本来の値に変換
  • %の後に、1バイトのコードの値が16進数文字列で存在するわけですから
    ポイントはその変換方法です

  • 以下は、%5C という文字列での説明です
  •  
    1) %5C から 5C\0 という文字列を作成
    2) 念のため大文字変換を行なう
    3) 1バイトのそれぞれの文字コードから 0x30 を減じます
    4) 数値文字列の場合は、その結果が 0x09 を超える事はありません
    5) 超える場合はアルファベットなので、さらに 7 を減じて正しい整数にします
    6) 計算して、本来のコードの値に戻します
    


    0x00 を利用した取り出し
  • 環境テーブルと同じような方法として、区切り文字を全て 0x00 に変換して
    おきます

  • 対象キーをそのテーブルよりサーチして、発見した場合は後続の値のアド
    レス取得してそれを戻す関数を作成してできるかぎり簡単に使えるように
    します


  • // **********************************************************
    // GET 取り出し
    // **********************************************************
    char *GET( char *name )
    {
    	return GetValueAddress( getbuffer, lstrlen( QUERY_STRING ), name );
    }
     
    // **********************************************************
    // POST 取り出し
    // **********************************************************
    char *POST( char *name )
    {
    	return GetValueAddress( postbuffer, atoi( CONTENT_LENGTH ), name );
    }