あ”ぁ寒い。


今年もなにかやり遂げたと言えるものがない年でした。反省。。。



そろそろホンキ出そうと思ってはみたものの、あれよあれよという間に年が明けそうだ。



そこで来年の目標は・・・



「いよいよホンキ出す!」

C言語でCGI作る人は今の時代、よほどの物好きか暇人か変わり者などと思われそうですがとにかく作るだけ作ってみましょ。。



処理としては、入力されたフォームデータを受け取ってURLデコードした文字が正しく表示できるかという単純なものです。



ごく単純な処理ではありますが、C言語で書くとなると一苦労です。開発効率悪すぎます。
でも開発効率悪いとグチるよりもどうやれば効率を上げられるか考えることは勉強になります。
効率を上げるための設計、ツール開発など自由にやっていいわけですから可能性は果てしないです。
そう、C言語はかつてUNIX上で使う言語として開発された後、UNIX自身を書くまでになった言語ですから・・

作っていて、もっとイケてるやり方があるとは思いましたがこれが今の私の限界です。



ということで以下がソースコードになります。



■Cソースコード(cgi.c)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_INPUT (1024*256)
#define HEADER "Content-type: text/html; charset=Shift_JIS\n\n"
#define HEX2C(c1,c2)    (((c1)-((c1) <= '9' ? 48 : (c1) <= 'F' ? 55 : 87))<<4| \
    ((c2)-((c2) <= '9' ? 48 : (c2) <= 'F' ? 55 : 87)))
#define EOT(expr) #expr "\n"


void add_alloc(char *s);


int main(void)
{
    void view(char *s);
    char *form_default(void);
    char *reqdata(char *s);
    void decode(void);
    char *p;
    char res[MAX_INPUT];



    decode();


    p= reqdata("act");


    if (strcmp(p, "next1") == 0)
    {
        sprintf(res, form_default(), "next1ページ", "next2", reqdata("t1"));
    }
    else if (strcmp(p, "next2") == 0)
    {
        sprintf(res, form_default(), "next2ページ", "next1", reqdata("t1"));
    }
    else
    {
        sprintf(res, form_default(), "トップページ", "next1", reqdata("t1"));
    }


    view(res);


    return 0;
}
char *form_default(void)
{
    return EOT(
<html><head>
<title>C言語で無謀にもCGI</title>
<head><body>
<h1>%s</h1>
<form action="cgi.exe" method="post">
<input type="text" name="t1">
<input type="hidden" name="act" value="%s">
<input type="submit" value="送信">
</form>
<hr>%s
</body>
</html>);
}
void decode(void)
{
    void _dec(char *s);
    char *p;
    size_t len= 0;


    if ((p=getenv("QUERY_STRING")) != NULL) // get
    {
        _dec(p);
    }
    if ((p=getenv("CONTENT_LENGTH")) != NULL) // post(not multipart)
    {
        len= atol(p);
        if (len > MAX_INPUT) view("多い");
        if ((p= (char *)malloc(len+1)) == NULL) return;
        add_alloc(p);
        fread(p, 1, len, stdin);
        *(p + len)= '\0';
        _dec(p);
    }


// return p;
}
void _dec(char *s) // URLエンコード文字をデコード
{
    void install(char *s);
    size_t l= 0;


    install(s);
    while (*s != '\0')
    {
        switch (*s){
        case '%': *(s - l)= HEX2C(*(s+1), *(s+2)); s+=2; l+=2; break;
        case '&': *(s - l)= '\0'; install(s-l+1); break;
        case '=': *(s - l)= '\0'; break;
        case '+': *(s - l)= ' '; break;
        default : *(s - l)= *s; break;
        }
        s++;
    }
    *(s - l)= '\0';
}
void view(char *s) // 画面表示
{
    void FREE(void);


    printf(HEADER "%s", s);
    FREE();
    exit(0);
}



#define MAX_PARAM 100
static char *data[MAX_PARAM]; // キー先頭を括り付けておく物干し竿


char *reqdata(char *key) // 問い合わせキーの値を返す
{
    int i;


    for (i=0; i<MAX_PARAM; i++) // キーを探索して、見つかれば値のアドレス返し
    {
        if (data[i] != NULL && strcmp(data[i], key) == 0) return data[i] + strlen(key) + 1;
    }


    return "\0";
}
void install(char *head) // キー先頭をセット
{
    static int i= 0;


    if (head != NULL && i < MAX_PARAM) data[i++]= head;
}



#define MAX_ALLOC 100
static char *alloc[MAX_ALLOC]; // ヒープ領域の先頭アドレスをまとめる


void add_alloc(char *s) // 動的に確保したヒープ領域の先頭アドレスを記憶
{
    static int i= 0;


    if (s != NULL && i < MAX_ALLOC) alloc[i++]= s;
}
void FREE(void) // 動的に確保したヒープ領域を開放
{
    int i= 0;


    while (alloc[i] != NULL) free(alloc[i++]);

}




■コンパイル

gcc cgi.c -o cgi.exe




■apacheの追加設定(C:\xampplite\apache\conf\httpd.conf)

<Directory "C:/xampplite/htdocs">
    Options Indexes FollowSymLinks Includes ExecCGI
          :
          :
AddHandler cgi-script .cgi .pl .exe
          :



設定ファイルhttpd.confを開き、「ExecCGI」と「.exe」を追加してapacheの再起動(設定変更を反映)




[表示結果] act=next1でもnext2でもない
そろそろホンキ出す-C言語 CGI top



[表示結果] act=next1
そろそろホンキ出す-C言語 CGI next1



[表示結果] act=next2
そろそろホンキ出す-C言語 CGI next2



今気づいたけど、このソースコードには脆弱性がある!!
表示文字のエンティティ変換してなかった。。大分単純化してしまいました。これまた面倒そうですねー



CGIは、呼ばれてからメモリにロード・実行される仕組みです。
なわけで、例えC言語と言えどもmod_perlやmod_phpといったapacheのモジュールとして組み込まれているものに比べるとどうしても処理の遅れをとる場合が多いです。



ちゅーことで、そのうちにapacheのモジュール作成に挑戦してみたいと思います。



最近のマヨネーズはフタが変わったせいか、きっちり閉めたつもりでもいつの間にか空気が入ってしまう。



空気が入るということは酸素が悪さして酸化を加速させるような気がしてどうも納得いかない。



私は言いたい!! キューピーさん、昔ながらの古きよきキャップに戻すってのはどうでしょ。。。