/*
 * Copyright (c) 1999 Sun Microsystems, Inc.
 * Copyright (c) 1999 Nihon Sun Microsystems K.K.
 * All rights reserved.
 */

/*
 * "$Id: csconv.c,v 1.1.1.1 2000/10/29 16:50:58 himi Exp $"
 */

#pragma ident	"@(#)csconv.c 1.1	99/04/27 SMI"


#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "csconv.h"


#define DEFAULT_BUF_SIZE (1024 * 1024 * 8)


char * ME;


void
usage(int exit_value)
{
	fprintf(stderr,
		"%s [-l locale] -f fromcode -t tocode [-o outfile] [file ...]\n",
		ME);

	exit(exit_value);
}


int
main(int argc, char ** argv)
{
	int		c;
	char *		locale;
	char *		tocode;
	char *		fromcode;
	csconv_t	cd;
	char *		ip;
	size_t		ileft;
	char *		op;
	size_t		oleft;
	char *		inbuf;
	size_t		inbytesleft;
	char *		outbuf;
	size_t		outbytesleft;
	int		len;
	int		fd;
	int		fd_out;
	char *		file_out;
	struct stat	st;
	char *		addr;

	extern char *	optarg;
	extern int 	optind;


	setlocale(LC_ALL, "");

	locale = NULL;
	tocode = NULL;
	fromcode = NULL;
	file_out = NULL;

	if (0 == argc) {
		fprintf(stderr, "0 == argc\n");
		exit(1);
	}

	if (NULL == (ME = strrchr(*(argv + 0), '/'))) {
		ME = *(argv + 0);
	}

	while (EOF != (c = getopt(argc, argv, "f:t:l:o:"))) {
		switch (c) {
		case 'l':
			if (NULL != locale) {
				usage(1);
			}
			locale = optarg;
			break;
		case 't':
			if (NULL != tocode) {
				usage(1);
			}
			tocode = optarg;
			break;
		case 'f':
			if (NULL != fromcode) {
				usage(1);
			}
			fromcode = optarg;
			break;
		case 'o':
			if (NULL != file_out) {
				usage(1);
			}
			file_out = optarg;
			break;
		default:
			break;
		}
	}

	if ((NULL == fromcode) || (NULL == tocode)) {
		usage(1);
	}

	if (NULL == locale) {
		cd = csconv_open(tocode, fromcode);
		if ((csconv_t)(-1) == cd) {
			perror("csconv_open");
			exit(1);
		}
	} else {
		cd = csconv_open_locale(locale, tocode, fromcode);
		if ((csconv_t)(-1) == cd) {
			perror("csconv_open_locale");
			exit(1);
		}
	}

	if (NULL == file_out) {
		fd_out = 1;
	} else {
		fd_out = open(file_out, O_CREAT|O_RDWR|O_TRUNC, 0666);
		if ((-1) == fd_out) {
			perror("open");
			fprintf(stderr, "%s: cannot optn %s to write\n",
				ME, file_out);
			exit(1);
		}
	}

	if (optind == argc) {
		inbuf = malloc(DEFAULT_BUF_SIZE);
		if (NULL == inbuf) {
			perror("malloc");
			exit(1);
		}
		inbytesleft = DEFAULT_BUF_SIZE;

		outbuf = malloc(DEFAULT_BUF_SIZE);
		if (NULL == outbuf) {
			perror("malloc");
			exit(1);
		}
		outbytesleft = DEFAULT_BUF_SIZE;

		ip = inbuf;
		ileft = inbytesleft;
		while(0 < (len = read(0, ip, ileft))) {
			ip += len;
			ileft -= len;
		}

		inbytesleft = (inbytesleft - ileft);

		ip = inbuf;
		ileft = inbytesleft;
		op = outbuf;
		oleft = outbytesleft;

		csconv(cd, (const char **)(&ip), &ileft, &op, &oleft);

		write(fd_out, outbuf, outbytesleft - oleft);

		free(inbuf);
		free(outbuf);
	}

	for ( ; optind < argc; optind++) {
		fd = open(*(argv + optind), O_RDONLY, 0);
		if ((-1) == fd) {
			perror("open");
			fprintf(stderr, "%s: failed to open %s\n",
				ME, *(argv + optind));
			continue;
		}
		if ((-1) == fstat(fd, &st)) {
			perror("fstat");
			fprintf(stderr, "%s: failed to stat %s\n",
				ME, *(argv + optind));
			close(fd);
			continue;
		}
		addr = mmap(0, st.st_size,
			    PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
		if (MAP_FAILED == addr) {
			perror("mmap");
			fprintf(stderr, "%s: failed to mmap %s\n",
				ME, *(argv + optind));
			close(fd);
			continue;
		}
		close(fd);

		inbuf = addr;
		inbytesleft = st.st_size;
		if (DEFAULT_BUF_SIZE < (st.st_size * 2)) {
			outbytesleft = (st.st_size * 2);
		} else {
			outbytesleft = DEFAULT_BUF_SIZE;
		}
		if (NULL == (outbuf = malloc(outbytesleft))) {
			perror("malloc");
			exit(1);
		}

		ip = NULL;
		ileft = 0;
		op = NULL;
		oleft = 0;

		csconv(cd, (const char **)(&ip), &ileft, &op, &oleft);

		ip = inbuf;
		ileft = inbytesleft;
		op = outbuf;
		oleft = outbytesleft;

		csconv(cd, (const char **)(&ip), &ileft, &op, &oleft);

		munmap(addr, st.st_size);

		write(fd_out, outbuf, outbytesleft - oleft);

		free(outbuf);
	}

	csconv_close(cd);

	if(NULL != file_out) {
		close(fd_out);
	}

	return 0;
}
