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;
}
知らない事だらけだ...
Posted at by



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によるプラグイン機能が実現出来ます。
意外と少ないコードで実装出来るので皆さん昔に作ったアプリケーション等を拡張して見られてはどうでしょうか。

Posted at by



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");
            struct json_object* body = json_object_object_get(item, "body");
            if (!is_error(todo_rid) && !is_error(body)) {
                char* _todo_rid = utf8_to_string_alloc(json_object_get_string(todo_rid));
                char* _body = utf8_to_string_alloc(json_object_get_string(body));
                printf("TODO-%03d [%s] %s\n", i+1, _todo_rid, _body);
                free(_todo_rid);
                free(_body);
            }
        }
    } else
    if (json_object_is_type(obj, json_type_object)) {
        struct json_object* todo_rid = json_object_object_get(obj, "todo_rid");
        if (!is_error(todo_rid)) {
            char* _todo_rid = utf8_to_string_alloc(json_object_get_string(todo_rid));
            if (_todo_rid) {
                printf("%s\n", _todo_rid);
                free(_todo_rid);
            }
        }
    }
    return 0;

error:
    if (user) free(user);
    if (pass) free(pass);
    return -1;
}
Posted at by