/*                                                            -*- C -*-
 * Copyright (c) 2003-2006  Motoyuki Kasahara
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#ifdef HAVE_ICONV_H
#include <iconv.h>
#endif

#if defined(HAVE_LIBCHARSET_H)
#include <libcharset.h>
#elif defined(HAVE_LANGINFO_H)
#include <langinfo.h>
#endif

/*
 * Convert `string' from EUC-JP to the current locale encoding, and
 * then write it to `stream'.
 */
int
fputs_eucjp_to_locale(const char *string, FILE *stream)
{
#if defined(HAVE_ICONV_OPEN) || defined(HAVE_LIBICONV_OPEN)
    size_t string_length;
    const char *locale_encoding;
    char *buffer = NULL;
    size_t buffer_size;
    iconv_t cd = (iconv_t)-1;
    const char *in_p;
    char *out_p;
    size_t in_left;
    size_t out_left;
    int fputs_result;

    string_length = strlen(string);

#if defined(HAVE_LOCALE_CHARSET)
    locale_encoding = locale_charset();
#elif defined(HAVE_NL_LANGINFO) && defined(CODESET)
    locale_encoding = nl_langinfo(CODESET);
#else
    locale_encoding = NULL;
#endif
    if (locale_encoding == NULL)
	goto failed;
    cd = iconv_open(locale_encoding, "eucJP");
    if (cd == (iconv_t)-1)
	cd = iconv_open(locale_encoding, "EUC-JP");
    if (cd == (iconv_t)-1)
	goto failed;

    buffer_size = (string_length + 1) * 2;

    for (;;) {
	buffer = malloc(buffer_size);
	if (buffer == NULL)
	    goto failed;

	in_p = string;
	in_left = string_length + 1;
	out_p = buffer;
	out_left = buffer_size;

	if (iconv(cd, &in_p, &in_left, &out_p, &out_left) != -1)
	    break;
	if (errno == E2BIG) {
	    /*
	     * Reset initial state.
	     * To avoid a bug of iconv() on Solaris 2.6, we set `in_left',
	     * `out_p' and `out_left' to non-NULL values.
	     */
	    in_left = 0;
	    out_p = buffer;
	    out_left = 0;
	    iconv(cd, NULL, &in_left, &out_p, &out_left);

	    free(buffer);
	    buffer = NULL;
	    buffer_size += string_length + 1;

	    continue;
	} else {
	    goto failed;
	}
    }

    iconv_close(cd);
    fputs_result = fputs(buffer, stream);
    free(buffer);

    return fputs_result;

    /*
     * An error occurs...
     */
  failed:
    if (cd != (iconv_t)-1)
	iconv_close(cd);
    if (buffer != NULL)
	free(buffer);
    return fputs(string, stream);

#else /* not HAVE_ICONV_OPEN && not HAVE_LIBICONV_OPEN */
    return fputs(string, stream);
#endif /* not HAVE_ICONV_OPEN && not HAVE_LIBICONV_OPEN */
}


/*
 * Convert `string' from EUC-JP to the current locale encoding, and
 * then write it and a newline to `stdout'.
 */
int
puts_eucjp_to_locale(const char *string)
{
    if (fputs_eucjp_to_locale(string, stdout) == EOF)
	return EOF;
    if (fputs_eucjp_to_locale("\n", stdout) == EOF)
	return EOF;

    return 0;
}
