2008/04/04

はてな
たぶん「ポインタ変数はポインタ型」で「配列変数は配列型」というイメージが持てないから。
C/C++のポインタの機能--変数の場所(アドレス) - builder by ZDNet Japan
int *n;
*n = 5; /* ポインタ変数nに値5を代入 */ 
現在は本文が修正されています。

C/C++のポインタの機能--配列との関係 - builder by ZDNet Japan

ポインタ変数と配列との深い関係を表す例を示そう。それは、配列の変数名をそのままポインタ変数名として扱えるということだ。


たぶん
int *n;
こう宣言するから
*n = 5;
こうしたくなる訳で
int* n;
こう宣言すれば、もしくは
typedef int * int_ptr
こうしておいて
int_ptr n;
こう宣言すれば
*n = 5;
こんな事が間違ってる事に気付くかな?
int m[] = {1,2,3,4};
int *n;
n = m;
ここでいうnへのmの代入は、mの先頭アドレスをnが指しているだけなのです。ポインタは「指し棒」!配列はメモリのかたまり!
※最近のC言語の本って「先頭アドレス」って表現あまり見ない気がする。

ちなみに、間違っても
char* n, m;
m = "Hello, World";
とか
#define char_ptr char *
...
char_ptr n, m;
m = "Hello, World";
しちゃ駄目(typedef使ってね)。

ちなみに...
char *s = "abc";
char s[] = "abc";
の話も前者が「ポインタ型」、後者が「配列型」と考えればいい。
つまり
typedef char char4[4];
char4 n = "abcef"; // char[4]型変数をchar[6]で初期化
こうすると、コンパイラによっては警告出力してくれる訳です。

2008/03/31

はてな
コンパイルは通るだろうけど、ちょっと本文から直してほしいですね。影響力のある場所でしかも「推薦する」なんてリンクが付いている状態で放置は間違いを広めてしまうよ。
C/C++のポインタの機能--参照渡しのような処理 - builder by ZDNet Japan
で、Electric Fenceの紹介につなげる記事にしようと思ったのですが
electric-fence-win32 - Google Code
Electric Fenceのwin32版なんてものを見つけてしまった。
てっきりUNIX版と同様、リンクすれば動くと思って色々試したけど、どうやらそうじゃないみたい。
//#include <efence.h>
#include <stdio.h>
int main( void ) {
    char *a = (char*)malloc(12);
    a[ 0] = 'H';
    a[ 1] = 'e';
    a[ 2] = 'l';
    a[ 3] = 'l';
    a[ 4] = 'o';
    a[ 5] = ',';
    a[ 6] = ' ';
    a[ 7] = 'W';
    a[ 8] = 'o';
    a[ 9] = 'r';
    a[10] = 'l';
    a[11] = 'd';
    a[12] = '\0';
    return 0;
}
オーバーランを検知してくれなかった。README.win32によると

Since you need to modify your own project anyway, simply add efence.c, page-win32.c, and print.c to your project.

と書いてありました。てっきりスタートアップルーチンを入れ替えてくれてくれる物かと思って少しだけ期待してしまいました。
mingw32ならば
Index: Makefile
===================================================================
--- Makefile    (revision 7)
+++ Makefile    (working copy)
@@ -9,7 +9,7 @@
 MAN_INSTALL_DIR= /usr/man/man3
 
 PACKAGE_SOURCE= README libefence.3 Makefile efence.h \
-   efence.c page.c print.c eftest.c tstheap.c CHANGES COPYING
+   efence.c page-win32.c print.c eftest.c tstheap.c CHANGES COPYING
 
 # Un-comment the following if you are running HP/UX.
 # CFLAGS= -Aa -g -D_HPUX_SOURCE -DPAGE_PROTECTION_VIOLATED_SIGNAL=SIGBUS
@@ -26,7 +26,7 @@
 # as well if using Sun's compiler, -static if using GCC.
 # CFLAGS= -g -Bstatic -DPAGE_PROTECTION_VIOLATED_SIGNAL=SIGBUS
 
-OBJECTS= efence.o page.o print.o
+OBJECTS= efence.o page-win32.o print.o
 
 all:   libefence.a tstheap eftest
    @ echo
@@ -63,7 +63,7 @@
 
 tstheap: libefence.a tstheap.o
    - rm -f tstheap
-   $(CC) $(CFLAGS) tstheap.o libefence.a -o tstheap -lpthread
+   $(CC) $(CFLAGS) tstheap.o libefence.a -o tstheap -lpthreadGC2
 
 eftest: libefence.a eftest.o
    - rm -f eftest
こんな風に修正して
mingw32-make CC=gcc libefence.a
でlibefence.aが出来上がり、上のソースの冒頭の「//」を外して
#include <efence.h>
#include <stdio.h>
int main( void ) {
    char *a = (char*)malloc(12);
    a[ 0] = 'H';
    a[ 1] = 'e';
    a[ 2] = 'l';
    a[ 3] = 'l';
    a[ 4] = 'o';
    a[ 5] = ',';
    a[ 6] = ' ';
    a[ 7] = 'W';
    a[ 8] = 'o';
    a[ 9] = 'r';
    a[10] = 'l';
    a[11] = 'd';
    a[12] = '\0';
    return 0;
}
あぶないコードに修正した後
gcc -g -o dame.exe dame.c -lefence
とすれば、efenceビルドされたdame.exeが出来上がり実行すると、正しくクラッシュしてくれる。gdbで確認すれば
C:¥temp¥electric-fence-win32>gdb dame.exe
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i686-pc-mingw32"...
(gdb) run
Starting program: C:¥temp¥electric-fence-win32/dame.exe
...
Program received signal SIGSEGV, Segmentation fault.
0x00401396 in main () at dame.c:17
17              a[12] = '\0';
(gdb)
とクラッシュ場所も分かると。でもUNIX版みたくソースは修正したくないなぁ。
あとmallocでなく
char a[12];
とした場合にクラッシュしてくれないのならば、威力半減ってところか。

2008/03/21

はてな
C言語される方は見ておいた方が良いかもしれない。
ロベールのC++入門講座を読んで C++ を初歩の初歩から再入門するよ - 前編 - ひげぽん OSとか作っちゃうかMona-
[] 演算子は a[b] と b[a] はおなじ意味らしい。なんと!
それぞれ *(a + b) 、*(b + a) になるので等しいのですね。
うむ。しらんかった。確かに出力されるアセンブリも
c-code-1
c-code-2
となり、結果同じ操作なのだけれど、まさか文法的にコレが通るとは思ってなかった。
int main(void) {
    int a[3];
    0[a] = 1;
    1[a] = 2;
    printf("%d,%d,%d,%d\n", a[0], a[1], 0[a], 1[a]);
    return 0;
}
知らない事だらけだ...

2008/03/19

はてな
私もこれまで色々なWindowsアプリケーションを作ってきましたが、それらの多くはデスクトップ上で目的の動作だけを実行する単純なアプリケーションだったりします。
最近のテキストエディタ等では、マクロ等と呼ばれる拡張言語を使用してエディタ本来の動作では実現出来ない色々な追加機能を実行する事が出来る様になっています。
今日は、既存のWin32アプリケーションにJavaScriptでマクロが実行出来る様にする為のtipsをご紹介。
拡張言語といってもJavaScriptの様に柔軟性のある言語を作り直すとなると程遠い工数を掛けてしまう事になりますが、Windowsには「ScriptControl」というスクリプト実行コンポーネントが用意されています。
今回はこれを使って外部にあるJavaScriptファイルを実行し、かつそのJavaScriptからアプリケーション内のオブジェクトを操作するまでを説明します。ScriptControlはCOMで実装されており、以下の様にインスタンスを生成します。
    hr = CoCreateInstance(
            CLSID_ScriptControl,
            NULL,
            CLSCTX_ALL,
            IID_IScriptControl,
            (void**)&pScriptCtrl);
そしてJavaScript(JScript)を実行させる為にLanguageプロパティを設定してExecuteStatementを実行します。
    hr = pScriptCtrl->put_Language(_bstr_t("JScript"));
    hr = pScriptCtrl->put_Timeout(-1);
    hr = pScriptCtrl->put_AllowUI(VARIANT_FALSE);
    hr = pScriptCtrl->ExecuteStatement(A2BSTR("var a = 'test'"));
これだけで既存のアプリケーションからJavaScriptが実行出来るようになります。
ただこれだけでは既存アプリケーションとの連携はまったく無く、面白味がありませんし単なる計算言語にしか成り得ません。
ブラウザ上で実行されるJavaScriptの様にwindowオブジェクトも無ければdocumentオブジェクトもありません。
つまりalertは使えません
このオブジェクトをJavaScript上に追加するのがAddObjectメソッドです。
AddObjectメソッドは名称指定でIUnknownオブジェクトをJavaScriptの実行スコープに追加出来ます。
ここに既存アプリケーションのオブジェクトを差し込む事になります。JavaScriptではメソッドを呼び出そうとする際にそのオブジェクトに対してIDispatchへのQueryInterfaceを試み、GetIDsOfNamesでdispIDを取得した後にInvokeメソッドを呼び出します。
この辺は、COMの知識のある方ならば既にご存知ですね。
で実装したIDispatchは以下の様なコードになりました。
class MyObject : public IDispatch
{
private:
    LONG m_cRef;

    // method type
    typedef HRESULT (MyObject::*Func)(DISPPARAMS*, VARIANT*);

    // method structure
    typedef struct _MY_OBJECT_METHOD_TABLE {
        _MY_OBJECT_METHOD_TABLE(DISPID dispid, const char* name, Func fn) {
            this->dispid = dispid;
            this->name = name;
            this->fn = fn;
        }
        DISPID dispid;
        const char* name;
        Func fn;
    } MY_OBJECT_METHOD_TABLE;

    // method table
    std::vector<MY_OBJECT_METHOD_TABLE> myObjectMethodTable;

public:
    // method: say
    //  show MessageBox
    HRESULT say(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            MessageBox(0, OLE2T(arg.bstrVal), _T("MyObject"), MB_OK);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // method: start
    //  start program by arguments
    HRESULT start(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            ShellExecute(NULL, _T("open"), OLE2T(arg.bstrVal), NULL, NULL, SW_SHOW);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // constructor
    MyObject() : m_cRef(0) {
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(1, "say", say));
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(2, "start", start));
    }

    STDMETHODIMP QueryInterface(REFIID rid, LPVOID *ppv) {
        *ppv = NULL;
        if (::IsEqualIID(rid, IID_IUnknown)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        if (::IsEqualIID(rid, IID_IDispatch)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    ULONG STDMETHODCALLTYPE AddRef() {
        ULONG ref = InterlockedIncrement(&m_cRef);
        return ref;
    }
    ULONG STDMETHODCALLTYPE Release() {
        ULONG ref = InterlockedDecrement(&m_cRef);
        return ref;
    }
    STDMETHODIMP GetTypeInfoCount(UINT *ptiCount) {
        if (ptiCount) *ptiCount = 0;
        return S_OK;
    }
    STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **pptInfo) {
        if (pptInfo) *pptInfo = NULL;
        return S_OK;
    }
    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID) {
        USES_CONVERSION;

        for(UINT n = 0; n < cNames; n++) {
            std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
            for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
                if (!strcmp(it->name, OLE2A(rgszNames[n]))) {
                    rgDispID[n] = it->dispid;
                    break;
                }
            }
            if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        }
        return S_OK;
    }
    STDMETHODIMP Invoke(DISPID dispID, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
        std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
        for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
            if (it->dispid == dispID) {
                return (this->*(it->fn))(pDispParams, pVarResult);
            }
        }
        if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        return S_OK;
    }
};
単純に引数の文字列をメッセージボックスで表示する「say」メソッドと、引数の文字列をプログラムとして起動する「start」メソッドを実装しています。
これを実行する「plugin.js」は以下の様になります。
MyObject.say('Hello');
MyObject.start('http://mattn.kaoriya.net');
これを実行すると「Hello」というメッセージボックスが表示された後、このサイトがブラウザで表示される結果となります。
20080319121041
全体のソースコードは以下の通り。
#include <atlbase.h>
#include <windows.h>
#include <string>
#include <vector>
#import <msscript.ocx> raw_interfaces_only, named_guids, no_namespace
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "shell32.lib")

class MyObject : public IDispatch
{
private:
    LONG m_cRef;

    // method type
    typedef HRESULT (MyObject::*Func)(DISPPARAMS*, VARIANT*);

    // method structure
    typedef struct _MY_OBJECT_METHOD_TABLE {
        _MY_OBJECT_METHOD_TABLE(DISPID dispid, const char* name, Func fn) {
            this->dispid = dispid;
            this->name = name;
            this->fn = fn;
        }
        DISPID dispid;
        const char* name;
        Func fn;
    } MY_OBJECT_METHOD_TABLE;

    // method table
    std::vector<MY_OBJECT_METHOD_TABLE> myObjectMethodTable;

public:
    // method: say
    //  show MessageBox
    HRESULT say(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            MessageBox(0, OLE2T(arg.bstrVal), _T("MyObject"), MB_OK);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // method: start
    //  start program by arguments
    HRESULT start(DISPPARAMS* pDispParams, VARIANT* ret) {
        USES_CONVERSION;

        for(int n = 0; n < pDispParams->cArgs; n++) {
            HRESULT hr;
            VARIANT arg;
            VariantInit(&arg);
            hr = VariantChangeType(&arg, &pDispParams->rgvarg[n], 0, VT_BSTR);
            ShellExecute(NULL, _T("open"), OLE2T(arg.bstrVal), NULL, NULL, SW_SHOW);
        }
        if (ret) {
            VariantInit(ret);
            V_VT(ret) = VT_BOOL;
            V_BOOL(ret) = VARIANT_TRUE;
        }
        return S_OK;
    }

    // constructor
    MyObject() : m_cRef(0) {
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(1, "say", say));
        myObjectMethodTable.push_back(MY_OBJECT_METHOD_TABLE(2, "start", start));
    }

    STDMETHODIMP QueryInterface(REFIID rid, LPVOID *ppv) {
        *ppv = NULL;
        if (::IsEqualIID(rid, IID_IUnknown)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        if (::IsEqualIID(rid, IID_IDispatch)) {
            *ppv = this;
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    ULONG STDMETHODCALLTYPE AddRef() {
        ULONG ref = InterlockedIncrement(&m_cRef);
        return ref;
    }
    ULONG STDMETHODCALLTYPE Release() {
        ULONG ref = InterlockedDecrement(&m_cRef);
        return ref;
    }
    STDMETHODIMP GetTypeInfoCount(UINT *ptiCount) {
        if (ptiCount) *ptiCount = 0;
        return S_OK;
    }
    STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **pptInfo) {
        if (pptInfo) *pptInfo = NULL;
        return S_OK;
    }
    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgDispID) {
        USES_CONVERSION;

        for(UINT n = 0; n < cNames; n++) {
            std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
            for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
                if (!strcmp(it->name, OLE2A(rgszNames[n]))) {
                    rgDispID[n] = it->dispid;
                    break;
                }
            }
            if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        }
        return S_OK;
    }
    STDMETHODIMP Invoke(DISPID dispID, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
        std::vector<MY_OBJECT_METHOD_TABLE>::iterator it = myObjectMethodTable.begin();
        for(it = myObjectMethodTable.begin(); it != myObjectMethodTable.end(); it++) {
            if (it->dispid == dispID) {
                return (this->*(it->fn))(pDispParams, pVarResult);
            }
        }
        if (it == myObjectMethodTable.end()) return E_UNEXPECTED;
        return S_OK;
    }
};

// load script and return the code
char* loadScriptAlloc(LPCTSTR pszFileName) {
    HANDLE hFile;
    DWORD dwFileSize, dwReadSize;
    char* pszData = NULL;

    hFile = CreateFile(
            pszFileName,
            GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);
    if (hFile == INVALID_HANDLE_VALUE) goto leave;

    dwFileSize = GetFileSize(hFile , NULL);
    if (dwFileSize == (DWORD)-1) goto leave;
    pszData = (char*)calloc(dwFileSize + 1, 1);
    if (!pszData) goto leave;

    if (!ReadFile(
            hFile,
            pszData,
            dwFileSize,
            &dwReadSize, NULL)) goto leave;
leave:
    if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
    return pszData;
}

int main(int argc, char *argv[]) {
    USES_CONVERSION;

    HRESULT hr = 0;
    IScriptControl* pScriptCtrl = NULL;
    char* pszCode = NULL;
    MyObject* pMyObject = NULL;

    CoInitialize(NULL);

    hr = CoCreateInstance(
            CLSID_ScriptControl,
            NULL,
            CLSCTX_ALL,
            IID_IScriptControl,
            (void**)&pScriptCtrl);
    if (FAILED(hr) || !pScriptCtrl) goto leave;

    hr = pScriptCtrl->put_Language(_bstr_t("JScript"));
    if (FAILED(hr)) goto leave;

    hr = pScriptCtrl->put_Timeout(-1);
    if (FAILED(hr)) goto leave;

    hr = pScriptCtrl->put_AllowUI(VARIANT_FALSE);
    if (FAILED(hr)) goto leave;

    pMyObject = new MyObject();
    hr = pScriptCtrl->AddObject(_bstr_t("MyObject"), pMyObject, VARIANT_TRUE);

    pszCode = loadScriptAlloc(_T("plugin.js"));
    if (!pszCode) goto leave;

    hr = pScriptCtrl->ExecuteStatement(A2BSTR(pszCode));
    free(pszCode);
    pszCode = NULL;

leave:
    if (pszCode) {
        free(pszCode);
        pszCode = NULL;
    }
    if (pScriptCtrl) {
        pScriptCtrl->Reset();
        pScriptCtrl->Release();
        pScriptCtrl = NULL;
    }
    if (pMyObject) {
        pMyObject->Release();
        pMyObject = NULL;
    }
    CoUninitialize();
    return 0;
}
あとはこの「say」メソッドなり「start」メソッドなりを既存アプリケーションとの連携用メソッドとして実装すれば、見事JavaScriptによるプラグイン機能が実現出来ます。
意外と少ないコードで実装出来るので皆さん昔に作ったアプリケーション等を拡張して見られてはどうでしょうか。

Visual C++プログラマのためのCOM入門―はじめるWindowsシステムプログラミング (DeV selection) Visual C++プログラマのためのCOM入門―はじめるWindowsシステムプログラミング (DeV selection)
豊田 孝
翔泳社 / ¥ 2,730 (1999-06)
 
発送可能時間:通常3~5週間以内に発送


2008/02/08

はてな
まぁ、id:tokuhirom氏が作ったwassr-todo.plがあるんですが...
(いいんです。C言語が書きたいんです!)

でっかく記事いっぱいにコードだけ...


ちなみに、json-cというライブラリとcURLを使っています。
昨日のパッチを使えばVC6でもビルド出来ます。
よろしければどうぞ。

最新ソースはCodeReposのコノ辺におきました。

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <curl/curl.h>
#include <json.h>

#define APP_NAME                   "wassr_todo"

#ifdef _WIN32
# ifndef snprintf
#  define snprintf _snprintf
# endif
#endif

#define TODO_MODIFY_URL "http://api.wassr.jp/todo/"
#define TODO_JSON_EXT ".json"

static char* response_cond = NULL;  /* response condition */
static char* response_mime = NULL;  /* response content-type. ex: "text/html" */
static char* response_data = NULL;  /* response data from server. */
static size_t response_size = 0;    /* response size of data */

static void initialize_http_response() {
    response_cond = NULL;
    response_mime = NULL;
    response_data = NULL;
    response_size = 0;
}

static void terminate_http_response() {
    if (response_cond) free(response_cond);
    if (response_mime) free(response_mime);
    if (response_data) free(response_data);
    response_cond = NULL;
    response_mime = NULL;
    response_data = NULL;
    response_size = 0;
}

static size_t handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream) {
    if (!response_data)
        response_data = (char*)malloc(size*nmemb);
    else
        response_data = (char*)realloc(response_data, response_size+size*nmemb);
    if (response_data) {
        memcpy(response_data+response_size, ptr, size*nmemb);
        response_size += size*nmemb;
    }
    return size*nmemb;
}

static size_t handle_returned_header(void* ptr, size_t size, size_t nmemb, void* stream) {
    char* header = NULL;

    header = (char*)malloc(size*nmemb + 1);
    memcpy(header, ptr, size*nmemb);
    header[size*nmemb] = 0;
    if (strncmp(header, "Content-Type: ", 14) == 0) {
        char* stop = header + 14;
        stop = strpbrk(header + 14, "\r\n;");
        if (stop) *stop = 0;
        if (response_mime) free(response_mime);
        response_mime = strdup(header + 14);
    }
    if (strncmp(header, "Last-Modified: ", 15) == 0) {
        char* stop = strpbrk(header, "\r\n;");
        if (stop) *stop = 0;
        if (response_cond) free(response_cond);
        response_cond = strdup(header);
    }
    if (strncmp(header, "ETag: ", 6) == 0) {
        char* stop = strpbrk(header, "\r\n;");
        if (stop) *stop = 0;
        if (response_cond) free(response_cond);
        response_cond = strdup(header);
    }
    free(header);
    return size*nmemb;
}

char* get_http_data(char* url, char* user, char* pass) {
    CURLcode res;
    CURL* curl;
    char* ret = NULL;
    int status = 0;
    char auth[512];

    initialize_http_response();

    memset(auth, 0, sizeof(auth));
    snprintf(auth, sizeof(auth)-1, "%s:%s", user, pass);

    curl = curl_easy_init();
    if (!curl) return NULL;
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, APP_NAME);
    res = curl_easy_perform(curl);
    res = res == CURLE_OK ? curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &status) : res;
    curl_easy_cleanup(curl);
    if (res == CURLE_OK && status == 200) {
        ret = (char*)malloc(response_size+1);
        memset(ret, 0, response_size+1);
        memcpy(ret, (char*)response_data, response_size);
    }

    terminate_http_response();

    return ret;
}

char* post_http_data(char* url, char* user, char* pass, char* data) {
    CURLcode res;
    CURL* curl;
    char* ret = NULL;
    int status = 0;
    char auth[512];

    initialize_http_response();

    memset(auth, 0, sizeof(auth));
    snprintf(auth, sizeof(auth)-1, "%s:%s", user, pass);

    curl = curl_easy_init();
    if (!curl) return NULL;
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_USERPWD, auth);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, handle_returned_data);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, handle_returned_header);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, APP_NAME);
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (void*)data);
    res = curl_easy_perform(curl);
    res = res == CURLE_OK ? curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &status) : res;
    curl_easy_cleanup(curl);
    if (res == CURLE_OK && status == 200) {
        ret = (char*)malloc(response_size+1);
        memset(ret, 0, response_size+1);
        memcpy(ret, (char*)response_data, response_size);
    }

    terminate_http_response();

    return ret;
}

char* string_to_utf8_alloc(const char* str) {
    if (!str) return NULL;
#ifdef WIN32
    UINT codePage;
    size_t in_len = strlen(str);
    codePage = GetACP();
    size_t wcssize = MultiByteToWideChar(codePage, 0, str, in_len,  NULL, 0);
    wchar_t* pszStrWC = (wchar_t*)malloc(sizeof(wchar_t)*(wcssize + 1));
    wcssize = MultiByteToWideChar(codePage, 0, str, in_len, pszStrWC, wcssize + 1);
    pszStrWC[wcssize] = '\0';

    codePage = CP_UTF8;
    size_t mbssize = WideCharToMultiByte(codePage, 0, pszStrWC, -1, NULL, 0, NULL, NULL);
    char* pszStrMB = (char*)malloc(mbssize + 1);
    mbssize = WideCharToMultiByte(codePage, 0, pszStrWC, -1, pszStrMB, mbssize, NULL, NULL);
    pszStrMB[mbssize] = '\0';

    free(pszStrWC);

    return pszStrMB;
#else
    return strdup(str);
#endif
}

char* utf8_to_string_alloc(const char* str) {
    if (!str) return NULL;
#ifdef WIN32
    UINT codePage = CP_UTF8;
    const char* ptr = str;
    if (str[0] == (char)0xef && str[1] == (char)0xbb && str[2] == (char)0xbf)
        ptr += 3;
    size_t wcssize = MultiByteToWideChar(codePage, 0, ptr, -1,  NULL, 0);
    wchar_t* pszStrWC = (wchar_t*)malloc(sizeof(wchar_t)*(wcssize + 1));
    wcssize = MultiByteToWideChar(codePage, 0, ptr, -1, pszStrWC, wcssize + 1);
    pszStrWC[wcssize] = '\0';

    codePage = GetACP();
    size_t mbssize = WideCharToMultiByte(codePage, 0, pszStrWC,-1, NULL, 0, NULL, NULL);
    char* pszStrMB = (char*)malloc(mbssize + 1);
    mbssize = WideCharToMultiByte(codePage, 0, pszStrWC, -1, pszStrMB, mbssize, NULL, NULL);
    pszStrMB[mbssize] = '\0';

    free(pszStrWC);

    return pszStrMB;
#else
    return strdup(str);
#endif
}

static char* url_encode_alloc(const char* str) {
    static const int force_encode_all = TRUE;
    const char* hex = "0123456789abcdef";

    char* buf = NULL;
    unsigned char* pbuf = NULL;
    int len = 0;

    if (!str) return NULL;
    len = strlen(str)*3;
    buf = (char*)malloc(len+1);
    memset(buf, 0, len+1);
    pbuf = (unsigned char*)buf;
    while(*str) {
        unsigned char c = (unsigned char)*str;
        if (c == ' ')
            *pbuf++ = '+';
        else if (c & 0x80 || force_encode_all) {
            *pbuf++ = '%';
            *pbuf++ = hex[c >> 4];
            *pbuf++ = hex[c & 0x0f];
        } else
            *pbuf++ = c;
        str++;
    }
    return buf;
}

char* add_todo(char* user, char* pass, char* body) {
    int size;
    char* body_utf8;
    char* body_data;
    char* post;
    char* data;

    body_utf8 = string_to_utf8_alloc(body);
    if (!body_utf8) return NULL;
    body_data = url_encode_alloc(body_utf8);
    free(body_utf8);
    size = 5 + strlen(body_data);
    post = (char*)malloc(size + 1);
    memset(post, 0, size + 1);
    strcpy(post, "body=");
    strcat(post, body_data);
    free(body_data);

    data = post_http_data("http://api.wassr.jp/todo/add.json", user, pass, post);
    free(post);
    return data;
}

char* modify_todo(char* user, char* pass, char* command, char* todo_rid) {
    int size;
    char* url;
    char* post;
    char* data;

    size = 9 + strlen(todo_rid);
    post = (char*)malloc(size + 1);
    memset(post, 0, size + 1);
    strcpy(post, "todo_rid=");
    strcat(post, todo_rid);

    size = strlen(TODO_MODIFY_URL) + strlen(command) + strlen(TODO_JSON_EXT);
    url = (char*)malloc(size + 1);
    memset(url, 0, size + 1);
    strcpy(url, TODO_MODIFY_URL);
    strcat(url, command);
    strcat(url, TODO_JSON_EXT);

    data = post_http_data(url, user, pass, post);
    free(post);
    free(url);
    return data;
}

char* list_todo(char* user, char* pass, int done_fg) {
    char* data;

    if (done_fg)
        data = get_http_data("http://api.wassr.jp/todo/list.json?done_fg=1", user, pass);
    else
        data = get_http_data("http://api.wassr.jp/todo/list.json?done_fg=0", user, pass);
    if (!data) {
        perror("Unknown server response(server down or invalid authenticate?)");
        return NULL;
    }
    return data;
}

int main(int argc, char* argv[]) {
    FILE* fp = NULL;
    char* home = NULL;
    char* user = NULL;
    char* pass = NULL;
    char conf[_MAX_PATH];
    char buff[BUFSIZ];
    char* data = NULL;
    struct json_object* obj = NULL;
    struct json_object* res = NULL;
    int i;

    home = getenv("HOME");
    if (!home) home = getenv("USERPROFILE");
    snprintf(conf, sizeof(conf), "%s/.wassr-todo", home);
    fp = fopen(conf, "rt");
    if (!fp) {
        perror("404 ~/.wassr-todo NOT FOUND");
        goto error;
    }
    while(fgets(buff, sizeof(buff), fp)) {
        char *ptr = strpbrk(buff, "\r\n");
        if (*ptr) *ptr = 0;
        if (!strncmp(buff, "username: ", 10))
            user = strdup(buff + 10);
        if (!strncmp(buff, "password: ", 10))
            pass = strdup(buff + 10);
    }
    fclose(fp);

    if (argc == 1)
        data = list_todo(user, pass, FALSE);
    else {
        if (argc == 2 && !strcmp(argv[1], "list"))
            data = list_todo(user, pass, FALSE);
        else
        if (argc == 2 && !strcmp(argv[1], "listdone"))
            data = list_todo(user, pass, TRUE);
        else
        if (argc == 3 && !strcmp(argv[1], "add"))
            data = add_todo(user, pass, argv[2]);
        else
        if (argc == 3 && (
                !strcmp(argv[1], "delete") ||
                !strcmp(argv[1], "start") ||
                !strcmp(argv[1], "stop") ||
                !strcmp(argv[1], "done")))
            data = modify_todo(user, pass, argv[1], argv[2]);
        else {
            puts("usage: wassr_todo [command] [argument]");
            puts("\t* command");
            puts("\t\tlist\t\tlist todo.");
            puts("\t\tlistdone\tlist todo which is done.");
            puts("\t\tadd [body]\tpost new todo to wassr.");
            puts("\t\tdelete [rid]\tdelete the todo.");
            puts("\t\tstart [rid]\tstart the todo.");
            puts("\t\tstop [rid]\tstop the todo.");
            puts("\t\tdone [rid]\tdone the todo.");
            goto error;
        }
    }

    if (!data) {
        perror("Unknown server response(server down or invalid authenticate?)");
        goto error;
    }
    obj = json_tokener_parse(data);
    free(data);

    if (is_error(obj)) {
        perror("Unknown server response(not json?)");
        goto error;
    }

    if (json_object_is_type(obj, json_type_object)) {
        res = json_object_object_get(obj, "error");
        if (!is_error(res)) {
            char* _res = utf8_to_string_alloc(json_object_get_string(res));
            if (_res) {
                printf("error: %s\n", _res);
                free(_res);
                goto error;
            }
        }
        res = json_object_object_get(obj, "message");
        if (!is_error(res)) {
            char* _res = utf8_to_string_alloc(json_object_get_string(res));
            if (_res) {
                printf("message: %s\n", _res);
                free(_res);
            }
        }
    }

    if (json_object_is_type(obj, json_type_array)) {
        for(i = 0; i < json_object_array_length(obj); i++) {
            struct json_object *item = json_object_array_get_idx(obj, i);
            struct json_object* todo_rid = json_object_object_get(item, "todo_rid");