/******************************************************************************
 * mod_uploader / CharCodeConverter.cpp
 ******************************************************************************
 * Copyright (C) 2005 Tetsuya Kimata <kimata@acapulco.dyndns.org>
 *
 * All rights reserved.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must
 *    not claim that you wrote the original software. If you use this
 *    software in a product, an acknowledgment in the product
 *    documentation would be appreciated but is not bcktuired.
 *
 * 2. Altered source versions must be plainly marked as such, and must
 *    not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 *    distribution.
 *
 * $Id: CharCodeConverter.cpp.in 535 2005-08-30 20:15:17Z svn $
 *****************************************************************************/

#include "CharCodeConverter.h"

#include "apu.h"
#include "apr_strings.h"

#if APU_HAVE_ICONV
#include <iconv.h>
#elif APU_HAVE_APR_ICONV
extern "C" {
#include <apr_iconv.h>
}
#endif

#ifdef DEBUG
#include <iostream>
#endif

const char CharCodeConverter::DEFAULT_CODE[]            = CHARACTER_CODE;
const unsigned char CharCodeConverter::EUCJP_PAT_FIRST  = 0xb5;
const unsigned char CharCodeConverter::SJIS_PAT_FIRST   = 0x8b;
const unsigned char CharCodeConverter::JIS_PAT_FIRST    = 0x1b;
const unsigned char CharCodeConverter::UTF8_PAT_FIRST   = 0xe4;


/******************************************************************************
 * public メソッド
 *****************************************************************************/
const char *CharCodeConverter::convert(apr_pool_t *pool, const char *str,
                                       const unsigned char *pat)
{
    return convert(pool, str, identify_code(pat), DEFAULT_CODE);
}

const char *CharCodeConverter::convert(apr_pool_t *pool, const char *str,
                                       const char *from, const char *to)
{
    char *buffer;
    char *converted;

    if (strcmp(from, to) == 0) {
        return apr_pstrdup(pool, str);
    }

#if APU_HAVE_ICONV
    @ICONV_CONST@ char *target;
    size_t str_len = strlen(str);
    size_t buffer_len = str_len * 3;
    iconv_t handle;

    target = static_cast<@ICONV_CONST@ char *>(apr_pstrdup(pool, str));
    converted = buffer = static_cast<char *>(apr_pcalloc(pool, buffer_len));
    buffer_len--;

    if ((handle = iconv_open(to, from)) == reinterpret_cast<iconv_t>(-1)) {
        throw apr_pstrcat(pool,
                          "文字コードの変換を行うコンバータが存在しません．",
                          "(", from, " -> ", to, ")", NULL);
    }
    iconv(handle, &target, &str_len, &buffer, &buffer_len);
    iconv_close(handle);
#elif APU_HAVE_APR_ICONV
    apr_size_t str_len = strlen(str);
    apr_size_t buffer_len = str_len * 3;
    apr_size_t converted_len = 0;
    apr_iconv_t handle;

    converted = buffer = static_cast<char *>(apr_pcalloc(pool, buffer_len));
    buffer_len--;

    if (apr_iconv_open(to, from, pool, &handle) != APR_SUCCESS) {
        throw apr_pstrcat(pool,
                          "文字コードの変換を行うコンバータが存在しません．",
                          "(", from, " -> ", to, ")", NULL);
    }
    apr_iconv(handle,
              &str, &str_len, &buffer, &buffer_len, &converted_len);
    apr_iconv_close(handle, pool);
#else
    // iconv，apr_iconv が無い場合，文字コード変換は行わない
    converted = apr_pstrdup(pool, str);
#endif

    return converted;
}

#include <stdio.h>

/******************************************************************************
 * private メソッド
 *****************************************************************************/
const char *CharCodeConverter::identify_code(const unsigned char *pat)
{
    switch (*pat) {
    case EUCJP_PAT_FIRST:   return "euc-jp";
    case SJIS_PAT_FIRST:    return "cp932";
    case JIS_PAT_FIRST:     return "iso-2022-jp";
    case UTF8_PAT_FIRST:    return "utf-8";
    default:
        throw "未知の文字コードです．";
    }
}


/******************************************************************************
 * テスト
 *****************************************************************************/
#ifdef DEBUG_CharCodeConverter
#include "apr_general.h"

void usage(const char *prog_name)
{
    cerr << "Usage: " << prog_name << "<FROM_CODE> <TO_CODE> <STRING>" << endl;
}

int main(int argc, const char * const *argv)
{
    apr_pool_t *pool;

    apr_app_initialize(&argc, &argv, NULL);
    apr_pool_create(&pool, NULL);

    try {
        if (argc != 4) {
            throw "文字コードと変換対象文字列が指定されていません．";
        }
        cout << CharCodeConverter::convert(pool, argv[3], argv[1], argv[2]) << endl;
    } catch(const char *message) {
        cerr << "Error: " << message << endl;
        usage(argv[0]);

        return EXIT_FAILURE;
    }

    apr_terminate();

    return EXIT_SUCCESS;
}
#endif

// Local Variables:
// mode: c++
// buffer-file-coding-system: euc-japan-dos
// End:
