/*
 * Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997.
 *
 * Any party obtaining a copy of this file from DSTC Pty Ltd, directly
 * or indirectly, is granted, free of charge, a full and unrestricted
 * irrevocable, world-wide, paid up, royalty-free, nonexclusive right and
 * license to deal in this software (the "Software"), including without
 * limitation the rights to use, copy, modify, merge, publish, distribute,
 * sublicense, and/or sell copies of the Software, and to permit persons
 * who receive copies from any such party to do so, with the only
 * requirement being that all copyright notices remain intact.
 *
 * This software is being provided "AS IS" without warranty of any kind,
 * express, implied or otherwise, including without limitation, any
 * warranty of merchantability or fitness for a particular purpose.
 *
 * In no event shall DSTC Pty Ltd be liable for any special, incidental,
 * indirect or consequential damages of any kind, or any damages whatsoever
 * resulting from loss of use, data or profits, whether or not advised of
 * the possibility of damage, and on any theory of liability, arising out
 * of or in connection with the use or performance of this software.
 *
 * DSTC Pty Ltd welcomes comments and bug fixes related to this software.
 * Please address any queries, comments or bug fixes (please include
 * the name and version of the software you are using and details
 * of your operating system version) to the address below:
 *
 *       DSTC Pty Ltd
 *       Level 7, Gehrmann Labs
 *       University of Queensland
 *       St Lucia, 4072
 *       Tel: +61 7 365 4310
 *       Fax: +61 7 365 4311
 *       Email: Enquiries@dstc.edu.au
 */

/*---------------------------------------------------------------------------*/

/*
 * to_ior, to_ior2 - simple test driver for CDR to IOR2 conversion.
 *
 * Usage: echo <ior> | to_ior2	# Convert normal IOR to IOR2 format.
 *	  echo <ior2> | to_ior	# Convert IOR2 format to normal IOR.
 *
 * The conversion direction is determined by looking at argv[0], so
 * the binary must be linked or copied to "to_ior" and "to_ior2" file names.
 *
 * Since this isn't meant to be a serious tool, conversions between normal
 * and IOR2 format are done via intermediate CDR. This is inefficient -
 * for industrial-strength use, it would be better to write direct
 * conversion code.
 * However, this is good enough to demonstrate the conversion from CDR to
 * IOR2 format and back, which is what is required by an ORB run-time.
 */

/*---------------------------------------------------------------------------*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<stdarg.h>
#include	<string.h>
#include	<errno.h>
#include	"ior2.h"

/*---------------------------------------------------------------------------*/

static const char 		*progname;
static const char * const	USAGE = "<in_IOR >out_IOR";
static int			codeval[256];

/*---------------------------------------------------------------------------*/

/*
 * Initialize codeval array for conversion from old IOR format to CDR.
 */

static void
init_codevals()
{
	codeval[(unsigned)'0'] = 0;
	codeval[(unsigned)'1'] = 1;
	codeval[(unsigned)'2'] = 2;
	codeval[(unsigned)'3'] = 3;
	codeval[(unsigned)'4'] = 4;
	codeval[(unsigned)'5'] = 5;
	codeval[(unsigned)'6'] = 6;
	codeval[(unsigned)'7'] = 7;
	codeval[(unsigned)'8'] = 8;
	codeval[(unsigned)'9'] = 9;
	codeval[(unsigned)'a'] = 10;
	codeval[(unsigned)'b'] = 11;
	codeval[(unsigned)'c'] = 12;
	codeval[(unsigned)'d'] = 13;
	codeval[(unsigned)'e'] = 14;
	codeval[(unsigned)'f'] = 15;
	codeval[(unsigned)'A'] = 10;
	codeval[(unsigned)'B'] = 11;
	codeval[(unsigned)'C'] = 12;
	codeval[(unsigned)'D'] = 13;
	codeval[(unsigned)'E'] = 14;
	codeval[(unsigned)'F'] = 15;
}

/*---------------------------------------------------------------------------*/

/*
 * Some systems provide a basename() call as part of libc. However,
 * basename() is not part of XPG4, so there is a version of it below
 * for systems that don't provide it.
 */

#if	!defined(HAS_BASENAME)

/*
 * Return the last element of the pathname in 'path', deleting any trailing
 * '/' characters. For example, basename("/usr/lib") is "lib", and
 * basename("/usr/") is "usr".
 * If 'path' is NULL, or *path is '\0', a pointer to the constant string "."
 * is returned. Pathnames consisting of only '/' characters return "/".
 *
 * Note: The return value may point into the argument string.
 *	 If 'path' has trailing '/' characters, these characters are
 *	 overwritten with '\0'.
 */

static char *
basename(char *path)
{
	char	*p;

	/*
	 * Check for NULL and empty path.
	 */
	if (path == NULL || *path == '\0')
		return(".");

	/*
	 * Replace trailing slashes with NULs.
	 */
	p = path + strlen(path) - 1;
	while (p >= path && *p == '/')
		*p-- = '\0';

	/*
	 * If the path contained only slashes, return "/".
	 */
	if (p < path)
		return("/");

	/*
	 * Scan from the end for a slash. If no slashes found, just return
	 * the path. Otherwise, return what follows the last slash.
	 */
	p = strrchr(path, '/');
	if (p == NULL)
		return(path);
	return(p + 1);
}

#endif	/* !defined(HAS_BASENAME) */

/*---------------------------------------------------------------------------*/

/*
 * Print a usage message and exit with status 'st'. If 'st' is zero, print
 * the message on stdout, stderr otherwise.
 */

static void
usage(int st)
{
	(void) fprintf(
		st != 0 ? stderr : stdout, "Usage: %s %s\n", progname, USAGE
	);
	exit(st);
	/* NOTREACHED */
}

/*---------------------------------------------------------------------------*/

/*
 * Print a message on stderr. Prepend the program name and append a
 * newline to the message, then exit.
 */

/* PRINTFLIKE1 */
static void
err(const char *format, ...)
{
	va_list ap;

	va_start(ap, format);
	(void) fprintf(stderr, "%s: ", progname);
	(void) vfprintf(stderr, format, ap);
	(void) putc('\n', stderr);
	va_end(ap);
	exit(EXIT_FAILURE);
	/* NOTREACHED */
}

/*---------------------------------------------------------------------------*/

/*
 * Grow a buffer by doubling its size.
 */

static void
grow_buf(char **buf, size_t *buf_size)
{
	*buf_size *= 2;
	if ((*buf = (char *)realloc((void *)*buf, *buf_size)) == NULL)
		err("Out of memory.");
}

/*---------------------------------------------------------------------------*/

int
main(int argc, char *argv[])
{
	char 		*inbuf = NULL;		/* Input buffer */
	size_t		inbuf_limit = 1024;	/* Initial size of inbuf */
	size_t		inbuf_len;		/* Num bytes in inbuf */
	unsigned char	*cdrbuf = NULL;		/* CDR buffer */
	size_t		cdrbuf_len;		/* Size of CDR buffer */
	unsigned char	*cdrp;			/* Cursor into cdrbuf */
	char		*outbuf = NULL;		/* Output buffer */
	size_t		outbuf_len;		/* Size of outbuf */
	int		c;			/* Current input char */
	char		*iorp;			/* Cursor into old IOR */
	unsigned	i;			/* Counter */
	int		to_ior2;		/* Direction of conversion */

	/*
	 * Set program basename for error messages.
	 */
	progname = basename(argv[0]);

	/*
	 * Check usage.
	 */
	if (argc == 2 && strcmp(argv[1], "-?") == 0)
		usage(EXIT_SUCCESS);
	else if (argc != 1)
		usage(EXIT_FAILURE);
	
	/*
	 * Initialize translation table.
	 */
	init_codevals();

	/*
	 * Figure out conversion direction.
	 */
	to_ior2 = strcmp(progname, "to_ior2") == 0;

	/*
	 * Create buffer space for input IOR.
	 */
	inbuf = (char *)malloc(inbuf_limit);
	if (inbuf == NULL)
		err("Out of memory.");

	/*
	 * Read input string.
	 */
	errno = 0;
	inbuf_len = 0;
	while ((c = getchar()) != EOF && c != '\n') {
		if (inbuf_len == inbuf_limit)
			grow_buf(&inbuf, &inbuf_limit);
		inbuf[inbuf_len++] = c;
	}
	inbuf[inbuf_len] = '\0';

	/*
	 * May have terminated the read loop early because of an error.
	 */
	if (errno != 0)
		err("Error reading input: %s", strerror(errno));

	/*
	 * Make buffer for CDR representation.
	 */
	if ((cdrbuf_len = cdr_size(inbuf)) == 0)
		err("Malformed input IOR: \"%s\"", inbuf);
	if ((cdrbuf = (unsigned char *)malloc(cdrbuf_len)) == NULL)
		err("Out of memory.");

	/*
	 * Convert to CDR.
	 */
	if (to_ior2) {
		/*
		 * Conversion from old IOR to CDR.
		 */
		iorp = inbuf;
		iorp += 4;			/* Skip IOR: prefix */
		cdrp = cdrbuf;
		cdrbuf_len = 0;
		while (*iorp != '\0' && *(iorp + 1) != '\0') {
			*cdrp = codeval[(unsigned)*iorp++] * 16;	
			*cdrp++ += codeval[(unsigned)*iorp++] % 16;
			cdrbuf_len++;
		}

		/*
		 * For conversion from old IOR format to IOR2, we allocate
		 * the same space as is occupied by the old IOR. Since
		 * IOR2 format is shorter, this will do.
		 */
		outbuf_len = inbuf_len;
	} else {
		/*
		 * Conversion from IOR2 to CDR.
		 */
		if (ior2_to_cdr(inbuf, (void *)cdrbuf, cdrbuf_len) == 0)
			err("Cannot convert IOR2 to CDR.");

		/*
		 * For conversion from IOR2 to old IOR format, we need
		 * 2 bytes for each CDR byte, plus 5 bytes for the prefix,
		 * plus room for the terminating NUL.
		 */
		outbuf_len = cdrbuf_len * 2 + 5 + 1;
	}

	/*
	 * Allocate output buffer space.
	 */
	if ((outbuf = (char *)malloc(outbuf_len)) == NULL)
		err("Out of memory.");

	/*
	 * Convert from CDR to output format.
	 */
	if (to_ior2) {
		outbuf_len = cdr_to_ior2(
			(void *)cdrbuf, cdrbuf_len, outbuf, outbuf_len
		);
		if (outbuf_len == 0)
			err("Cannot convert from CDR to IOR2.");
	} else {
		iorp = outbuf;
		*iorp++ = 'I';
		*iorp++ = 'O';
		*iorp++ = 'R';
		*iorp++ = ':';
		for (i = 0; i != cdrbuf_len; i++) {
			(void) sprintf(iorp, "%02X", cdrbuf[i]);
			iorp += 2;
		}
	}

	/*
	 * Print output IOR.
	 */
	if (fputs(outbuf, stdout) == EOF)
		err("Error writing output: %s", strerror(errno));
	if (fputc('\n', stdout) == EOF)
		err("Error writing output: %s", strerror(errno));
	if (fclose(stdout) == EOF)
		err("Error closing stdout: %s", strerror(errno));

	/*
	 * Clean up.
	 */
	free((void *)outbuf);
	free((void *)cdrbuf);
	free((void *)inbuf);

	return EXIT_SUCCESS;
}
