C 言語で CGI!

C 言語で CGI です。どうなんですかね。ともかく、ものは試しです。

getenv  という関数を使えば REQUEST_METHOD, QUERY_STRING といった CGI プログラミングで必要になる環境変数にアクセスできるようです。

それじゃあということで QUERY_STRING を分割して連結リストに格納する関数を書いてみたんですが、それだけでもめんどくさい。そして気付いてしまったんですが、テンプレートは一体どうするの?まさか fread, printf だけでやるのは厳しいだろうし…。

そこで C で使えるテンプレートエンジンを探したところ、Neotonic ClearSilver という清涼飲料水みたいな名前のエンジンを見つけました。これのいいところは、CGI Kit という CGI を書くのに必要な機能をまとめたモジュールが入ってるところです。

早速 ClearSilver のインストールです。Fedora 16 では sudo yum install clearsilver-devel で OK です。ただし、ヘッダファイルを若干修正する必要がありました。以下のヘッダファイルに登場する #include “xxx/yyy.h” を全て #include “../xxx/yyy.h” に書き換えます (xxx は cgi, cs, util のいずれか):

  • /usr/include/ClearSilver/cgi/cgi.h
  • /usr/include/ClearSilver/cgi/cgiwrap.h
  • /usr/include/ClearSilver/cgi/date.h
  • /usr/include/ClearSilver/cgi/html.h
  • /usr/include/ClearSilver/cs/cs.h
  • /usr/include/ClearSilver/util/dict.h
  • /usr/include/ClearSilver/util/filter.h
  • /usr/include/ClearSilver/util/neo_date.h
  • /usr/include/ClearSilver/util/neo_err.h
  • /usr/include/ClearSilver/util/neo_files.h
  • /usr/include/ClearSilver/util/neo_hash.h
  • /usr/include/ClearSilver/util/neo_hdf.h
  • /usr/include/ClearSilver/util/neo_misc.h
  • /usr/include/ClearSilver/util/neo_net.h
  • /usr/include/ClearSilver/util/neo_rand.h
  • /usr/include/ClearSilver/util/neo_server.h
  • /usr/include/ClearSilver/util/neo_str.h
  • /usr/include/ClearSilver/util/rcfs.h
  • /usr/include/ClearSilver/util/skiplist.h
  • /usr/include/ClearSilver/util/ulist.h
  • /usr/include/ClearSilver/util/ulocks.h
  • /usr/include/ClearSilver/util/wildmat.h

では環境変数を表示する CGI を作ります。まず index.c です:


#include <ClearSilver/ClearSilver.h>

int
main ()
{
    CGI *cgi;
    HDF *hdf;
    CSPARSE *cs;

    nerr_log_error (hdf_init (&hdf));
    nerr_log_error (hdf_set_value (hdf, "Page.Title", "私のホームページへようこ そ!"));
    nerr_log_error (cgi_init (&cgi, hdf));
    nerr_log_error (cgi_parse (cgi));
    nerr_log_error (cgi_cs_init (cgi, &cs));
    nerr_log_error (cgi_display (cgi, "layout.html"));
    cs_destroy (&cs);
    cgi_destroy (&cgi);
    /* hdf_destroy (&hdf); */ /* hdf を破壊すると double free で core dump */
    return 0;
}

続いてテンプレートファイルの layout.html です:

<!doctype html>
<html>
    <head>
        <title><?cs var:Page.Title ?></title>
    </head>
    <body>
        <h1><?cs var:Page.Title ?></h1>
        <h2>Query.*</h2>
        <ul>
            <?cs each:item = Query ?>
            <li><?cs name:item ?> - <?cs var:item ?></li>
            <?cs /each ?>
        </ul>

        <h2>HTTP.*</h2>
        <ul>
            <?cs each:item = HTTP ?>
            <li><?cs name:item ?> - <?cs var:item ?></li>
            <?cs /each ?>
        </ul>

        <h2>CGI.*</h2>
        <ul>
            <?cs each:item = CGI ?>
            <li><?cs name:item ?> - <?cs var:item ?></li>
            <?cs /each ?>
        </ul>
    </body>
</html>

コンパイルは次のようにします (libz が必要なので、 -l オプションで z を指定しています):

gcc -Wall -pedantic -o index.cgi -lz index.c /usr/lib/libneo_*.a

それから出来上がった index.cgi に web サーバ経由でアクセスすると、こんな風になります (action=hoge というクエリ文字列を渡してあります):

welcome to my homepage

テンプレートを編集すると、再コンパイルの必要もなく変更がすぐに反映されます。すてき。

続いてメニューつきの簡単なサイトを作ってみました。メニューの内容は hdf ファイルにまとめてみました。

まず index.c です:

#include <ClearSilver/ClearSilver.h>
#include <string.h>

void action_about (HDF *hdf);
void action_index (HDF *hdf);
NEOERR * my_renderer (void *user_data, char *string);
void set_title (HDF *hdf, const char *path);

int
main ()
{
    CGI *cgi;
    HDF *hdf;
    CSPARSE *cs;
    char *action;

    nerr_log_error (hdf_init (&hdf));
    nerr_log_error (hdf_read_file (hdf, "menu.hdf")); /* メニュー読み込み */
    nerr_log_error (cgi_init (&cgi, hdf));
    nerr_log_error (cgi_parse (cgi));
    nerr_log_error (cgi_cs_init (cgi, &cs));

    action = hdf_get_value (hdf, "Query.action", "");

    if (! strcmp (action, "about")) {
        action_about (hdf);
    } else if (! strcmp (action, "index") || ! strlen (action)) {
        action_index (hdf);
    } else {
        /*
         * ここでは文字列を直にパースしてますが、
         * cs_parse_file でテンプレートファイルを読み込むこともできるはずです
         */
        char *content = "Status: 404 Not Found\nContent-Type: text/plain\n\n404\n";

        nerr_log_error (cs_parse_string (cs, content, strlen (content)));
        nerr_log_error (cs_render (cs, NULL, my_renderer));
        goto quit;
    }

    nerr_log_error (cgi_display (cgi, "layout.html"));

quit:
    /* 下手に cs, cgi, hdf を destroy すると core dump するようです */
    return 0;
}

void
action_about (HDF *hdf)
{
    set_title (hdf, "Page.Menu.About.Title");
    nerr_log_error (hdf_set_value (hdf, "Page.Content", "about.html"));
}

void
action_index (HDF *hdf)
{
    set_title (hdf, "Page.Menu.Index.Title");
    nerr_log_error (hdf_set_value (hdf, "Page.Content", "index.html"));
}

NEOERR *
my_renderer (void *user_data, char *string)
{
    printf ("%s", string);
    return NULL;
}

void
set_title (HDF *hdf, const char *path)
{
    char *title;

    title = hdf_get_value (hdf, path, "");
    nerr_log_error (hdf_set_value (hdf, "Page.Title", title));
}

続いて menu.hdf です:

Page.Menu {
    Index {
        Name = ホーム
        URL = ?
        Title = 私のホームページへようこそ!
    }
    About {
        Name = このサイトについて
        URL = ?action=about
        Title = このサイトについて
    }
}

あとはテンプレートファイルです。まず基底になる layout.html:

<!doctype html>
<html>
    <head>
        <title><?cs var:Page.Title ?></title>
        <style type=text/css>
        ul.menu { padding: 0px }
        ul.menu > li { float: left; margin-left: 30px }
        </style>
    </head>
    <body>
        <ul class="menu">
            <?cs each:item = Page.Menu ?>
            <li><a href="<?cs var:item.URL ?>"><?cs var:item.Name ?></a></li>
            <?cs /each ?>
        </ul>
        <div style="clear:both"></div>
        <h1><?cs var:Page.Title ?></h1>
        <?cs include:Page.Content ?>
    </body>
</html>

次に index.html (無用な混乱を避けるために、apache の DirectoryIndex に index.cgi を指定しておきます):

        <h2>Query.*</h2>
        <ul>
            <?cs each:item = Query ?>
            <li><?cs name:item ?> - <?cs var:item ?></li>
            <?cs /each ?>
        </ul>

        <h2>HTTP.*</h2>
        <ul>
            <?cs each:item = HTTP ?>
            <li><?cs name:item ?> - <?cs var:item ?></li>
            <?cs /each ?>
        </ul>

        <h2>CGI.*</h2>
        <ul>
            <?cs each:item = CGI ?>
            <li><?cs name:item ?> - <?cs var:item ?></li>
            <?cs /each ?>
        </ul>

最後に about.html:

<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

index.cgi?action=about にアクセスしてみました:

about this site

いいですねー。なんだか PHP 並のお手軽さになった気がします。あとはセッション管理とかユーザ認証とかですね。でも、 さすがの ClearSilver もそこまで面倒見てくれません。残念。

ともあれ、ClearSilver があれば、C でかなり簡単に CGI が作れることが分かりました。

(コウヅ)

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中