/******************************************************************************
 * mod_uploader / PlainConverter.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 required.
 *
 * 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: PlainConverter.cpp 678 2005-09-14 20:40:24Z svn $
 *****************************************************************************/

#include "PlainConverter.h"
#include "UploadItemReader.h"
#include "UploadItem.h"
#include "CharCodeConverter.h"
#include "Auxiliary.h"
#include "Misc.h"

#include "apr_strings.h"

#include <iostream>
#include <iomanip>


/******************************************************************************
 * public メソッド
 *****************************************************************************/
PlainConverter::PlainConverter(apr_pool_t *pool)
    : pool_(pool)
{

}

void PlainConverter::convert(const char *src_path, const char *plain_dir_path,
                             const char *code, bool is_quiet)
{
    apr_filetype_e type;

    type = get_file_type(src_path);

    if (type == APR_DIR) {
        convert_dir(src_path, plain_dir_path, code, is_quiet);
    } else {
        convert_file(src_path, plain_dir_path, code, is_quiet);
    }
}

void PlainConverter::convert_dir(const char *upload_dir_path,
                                 const char *plain_dir_path,
                                 const char *code, bool is_quiet)
{
    UploadItemReader reader(pool_, upload_dir_path);
    apr_dir_t *dir;
    apr_finfo_t info;

    if (apr_dir_open(&dir, upload_dir_path, pool_) != APR_SUCCESS) {
        throw "アップロードファイルのリストを読み込めませんでした．";
    }

    while (apr_dir_read(&info, APR_FINFO_NAME, dir) == APR_SUCCESS) {
        if (info.name[0] == '.') {
            continue;
        }

        try {
            convert_file(reader, info.name, plain_dir_path, code, is_quiet);
        } catch(const char *message) {
            cerr << "Error: " << message << endl;
        }

    }

    apr_dir_close(dir);
}

void PlainConverter::convert_file(const char *upload_file_path,
                                  const char *plain_dir_path,
                                  const char *code, bool is_quiet)
{
    UploadItemReader reader(pool_, dirname_ex(pool_, upload_file_path));

    convert_file(reader, basename_ex(upload_file_path), plain_dir_path,
                 code, is_quiet);
}


/******************************************************************************
 * private メソッド
 *****************************************************************************/
void PlainConverter::convert_file(UploadItemReader &reader,
                                  const char *upload_file_name,
                                  const char *plain_dir_path,
                                  const char *code, bool is_quiet)
{
    UploadItem::header *header;
    apr_file_t *upload_file;
    apr_file_t *plain_file;
    const char *plain_path;
    char buffer[RFC1867_READ_BLOCK_SIZE];
    apr_size_t size = sizeof(buffer);

    try {
        upload_file = NULL;
        plain_file = NULL;

        header = reader.read(upload_file_name, &upload_file);

        plain_path = create_file_path(plain_dir_path, header->file_name, code);

        if (!is_quiet) {
            cerr << upload_file_name << " -> " << plain_path << endl;
        }

        if (apr_file_open(&plain_file, plain_path,
                          APR_READ|APR_WRITE|APR_CREATE|APR_BINARY,
                          APR_OS_DEFAULT, pool_) != APR_SUCCESS) {
            throw "コピー先のファイルを開けませんでした．";
        }

        while (apr_file_read_full(upload_file, buffer, size, &size) == APR_SUCCESS) {
            if (apr_file_write_full(plain_file, buffer, size, &size) != APR_SUCCESS) {
                throw "コピー先のファイルに書き込めませんでした．";
            }
            size = sizeof(buffer);
        }

        if (apr_file_eof(upload_file) != APR_EOF) {
            throw "アップロードファイルの読み出しに失敗しました．";
        }

        apr_file_close(plain_file);
        apr_file_close(upload_file);
    } catch(const char *message) {
        if (plain_file != NULL) {
            apr_file_close(plain_file);
        }
        if (upload_file != NULL) {
            apr_file_close(upload_file);
        }

        throw;
    }
}

const char *PlainConverter::create_file_path(const char *plain_dir,
                                             const char *plain_name,
                                             const char *code)
{
    const char *path;
    const char *path_template;
    char *modified_path;

    if (apr_filepath_merge(const_cast<char **>(&path), plain_dir, plain_name,
                           APR_FILEPATH_NOTABOVEROOT, pool_) != APR_SUCCESS) {
        throw "コピー先のファイルの名前を構成できませんでした．";
    }

    path = CharCodeConverter::convert(pool_, path,
                                      CharCodeConverter::DEFAULT_CODE, code);

    if (get_file_type(path) == APR_NOFILE) {
        return path;
    }

    path_template = apr_pstrcat(pool_, path, ".%d", NULL);
    for (apr_size_t i = 1; i < 100; i++) {
        modified_path = apr_psprintf(pool_, path_template, i);

        if (get_file_type(modified_path) == APR_NOFILE) {
            return modified_path;
        }
    }

    throw "ファイル名が同一のファイルが多すぎます．";
}

apr_filetype_e PlainConverter::get_file_type(const char *path)
{
    apr_finfo_t info;

    if (apr_stat(&info, path, APR_FINFO_TYPE, pool_) != APR_SUCCESS) {
        return APR_NOFILE;
    }

    return info.filetype;
}


/******************************************************************************
 * main 関数
 *****************************************************************************/
#include "apr_general.h"
#include "apr_pools.h"
#include "apr_getopt.h"


#include <iostream>

static const apr_getopt_option_t option_list[] = {
    { "code",   'c',    1 },
    { "quiet",  'q',    0 },
    { "help",   'h',    0 },
    { NULL,     0,      0 }
};

void usage(const char *prog_name)
{
    cerr << "Usage: " << prog_name << " [options] SRC DST" << endl;
    cerr << "Options are:" << endl;
    cerr << "  " << "-c, --code        The character code of filename. (default: euc-jp)" << endl;
    cerr << "  " << "-q, --quiet       Do not out sledding information." << endl;
    cerr << "  " << "-h, --help        Display usage information. (this message)" << endl;
}

void get_option(apr_pool_t *pool,
                int argc, const char * const *argv,
                const char **src, const char **dst,
                bool *is_quit, const char **code)
{
    apr_getopt_t* opt;
    int opt_char;
    const char *opt_string;
    apr_status_t status;

    apr_getopt_init(&opt, pool, argc, argv);

    while ((status = apr_getopt_long(opt, option_list, &opt_char, &opt_string)) == APR_SUCCESS) {
        switch (opt_char) {
        case 'c':
            *code = opt_string;
            break;
        case 'q':
            *is_quit = true;
            break;
        case 'h':
            usage(argv[0]);
            exit(EXIT_FAILURE);
        }
    }
    if (status != APR_EOF) {
        return;
    }

    if ((opt->argc-opt->ind) != 2) {
        usage(argv[0]);
        exit(EXIT_FAILURE);
    }

    *src = (opt->argv)[opt->ind];
    *dst = (opt->argv)[opt->ind+1];
}

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

    const char *src;
    const char *dst;
    const char *code = CharCodeConverter::DEFAULT_CODE;
    bool is_quiet = false;

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

    get_option(pool, argc, argv, &src, &dst, &is_quiet, &code);

    if (!is_quiet) {
        cout << setw(70) << setfill('-') << '-' << endl;
        cout << "Setting:" << endl;
        cout << "  source        = " << src << endl;
        cout << "  destination   = " << dst << endl;
        cout << "  filename code = " << code << endl;
        cout << setw(70) << setfill('-') << '-' << endl;
    }

    PlainConverter converter(pool);
    try {
        converter.convert(src, dst, code, is_quiet);
    } catch(const char *message) {
        cerr << "Error: " << message << endl << endl;
        usage(argv[0]);

        return EXIT_FAILURE;
    }

    apr_terminate();

    return EXIT_SUCCESS;
}

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