そもそも
Query Stringとは何でしょうか?
URLの後ろの方に「?」文字があるのをご覧になったことがあると思います.
その「?」文字よりさらに後ろにある文字列の部分をQuery Stringと呼びます.
Query Stringは name=val という形式のデータの羅列となっており、これが複数ある場合はさらに & 文字でこれらを区切ります.
つまり一般に、name1=val1
&name2=val2
&name3=val3 … という形式になっています.
通常はnameの部分にそのスクリプトにとってなんらかの意味があってわかりやすい名前をつけます.
そして = 文字に続けてそれに対する値を付加することで、CGIスクリプトに対する引数を指定する形になります.
Query Stringの内容をCGIスクリプト側で獲得するにはどうすればよいでしょうか?
これは
環境変数QUERY_STRINGを参照します.
我々は既に一つ前のサンプルでMoai CGIにおける全環境変数を取得していました.
今回も cgi_util にご登場願いましょう.
CGIEVar_createによってCGIEVarを作成すると、これの query_string_ メンバにQuery Stringの値が格納されています.
これを直接参照してもよいのですが、NULL値になっている場合のチェックが面倒なので、
cgi_util が提供する関数マクロ CGIUtil_getQueryString を使いましょう.
この関数マクロには CGIEVar を引数として渡します.
次の処理として、この query_string の解析を行います.
これには CGIUtil_getQueryStringToken を使いましょう.
この関数は、query_string を & 文字で分割し、それぞれの「key=val」という形式の塊(Token)において、そのkeyとvalにあたる部分を取得します.
この関数の第2引数では、それらの塊(Token)の何番目を取得したいかを指定します.
それでは今回も例によってキーポイントとなるコードを示して終わりとしましょう.
CGIEVar* evar = CGIEVar_create();
const char* query_string = CGIUtil_getQueryString( evar );
...
{
size_t i = 0;
char key[ 256 ] = "";
char val[ 256 ] = "";
for( i=0; i<64; ++i ){
int result = CGIUtil_getQueryStringToken( query_string, i,
key, sizeof(key),
val, sizeof(val) );
if( result == 0 ){ break; }
printf( "%s = [%s]\n", rejectHtmlTag(key), rejectHtmlTag(val) );
}
}
...
CGIEVar_destroy( evar );
Query Stringについては以上となります.
補足事項
少し話が脱線しますが C言語で環境変数を取得する関数として getenv を思い浮かべる方もおられると思います.
ですが上述した通り、我々はこの関数を直接使うべきではありません. 安易に使うと大変危険なためです.
使うのはこの関数の特性を十分理解した上で、環境変数の取得/設定を行うラッパーを自作するような仕方のない場合に限定すべきです.
簡単に理由を説明しましょう.
例えば2つの環境変数QUERY_STRINGとHTTP_COOKIEの値を取得したいからといって以下のようなコードを記述しては
いけません!
char* query_string = getenv(
"QUERY_STRING" );
char* http_cookie = getenv(
"HTTP_COOKIE" );
printf(
"query_string=[%s] http_cookie=[%s]",
rejectHtmlTag(query_string), rejectHtmlTag(http_cookie) );
パッと見では何の問題もなさそうに見えるかもしれませんが、実はgetenvを不用意に連続させていることが問題です.
その根拠は The C Standard, 7.22.4.6, paragraph 4 [ISO/IEC 9899:2011] において getenv に関する次のような記述に拠ります.
The getenv function returns a pointer to a string associated with the matched list member.
The string pointed to shall not be modified by the program but may be overwritten by a subsequent call to the getenv function.
つまり上の「NG!」の時点のgetenv呼び出しで早くもquery_stringの示すメモリ領域は無効化している恐れがあります.
これ以降でquery_stringにアクセスすると
未定義の動作を引き起こすことでしょう.
従ってgetenvで取得した値は、即座に別の文字列バッファにでもコピーしておかなければなりません.
以下にstrdupを使った修正例を示しましょう. あらかじめEnvVar_get関数を下請けとして定義しておきます.
char* EnvVar_get( const char* varname ){
char* tmp = getenv( varname );
return tmp ? strdup( tmp ) :
NULL;
}
...
char* query_string = EnvVar_get(
"QUERY_STRING");
char* http_cookie = EnvVar_get(
"HTTP_COOKIE");
printf(
"query_string=[%s] http_cookie=[%s]",
rejectHtmlTag(query_string), rejectHtmlTag(http_cookie) );
if( query_string ){ free( query_string ); }
if( http_cookie ){ free( http_cookie ); }
マルチスレッドの場合にも万全を期すなら、EnvVar_get内の開始と終わりにおいてGlobalMutexなどでlock/unlockを掛ける必要があります.
環境変数を設定する関数にputenvというものもありますが、これも同様に(むしろgetenv以上に)深刻な問題があります.
libZnk が提供する関数 ZnkEnvVar_get と ZnkEnvVar_set はgetenvとputenvに関するこの種の問題を解消したものです.