--- /dev/null	2013-02-23 16:58:58.000000000 +0000
+++ new/usr/src/cmd/iconv/iconv.c	2013-02-23 16:58:58.083199742 +0000
@@ -0,0 +1,435 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2013 David Hoeppner.  All rights reserved.
+ */
+
+/*
+ * POSIX iconv.
+ */
+
+#include <sys/list.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <iconv.h>
+#include <langinfo.h>
+#include <libnvpair.h>
+#include <locale.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "iconv.h"
+
+static const char *g_progname = "iconv";
+
+static char	*g_from_cs = "UTF-8";
+static char	*g_to_cs = "UTF-8";
+static iconv_t	g_ich;			/* iconv(3c) lib handle */
+static int	g_errcnt;
+static boolean_t g_cflag = B_FALSE;	/* Skip invalid characters */
+static boolean_t g_sflag = B_FALSE;	/* Silent */
+static boolean_t g_lflag = B_FALSE;	/* List conversions */
+
+
+/*
+ * Forward declarations.
+ */
+static void	usage(void) __NORETURN;
+static void	do_iconv(FILE *, const char *);
+static void	list_codesets(void);
+int		yyparse(void);
+
+typedef struct _iconv_item {
+	list_node_t	ii_next;
+	list_t		ii_alias_list;
+	char		*ii_name;
+} iconv_item_t;
+
+typedef struct _iconv_alias {
+	list_node_t	ia_next;
+	char		*ia_name;
+} iconv_alias_t;
+
+/*
+ * Print usage.
+ */
+static void
+usage(void)
+{
+	/* XXX g_progname */
+	(void) fprintf(stderr, _(
+	    "usage:\ticonv [-cs] [-f fromcode] [-t tocode] [file ...]\n"
+	    "\ticonv [-cs] -f frommap -t tomap [file ...]\n"));
+	(void) fprintf(stderr, _("\t%s -l\n"), g_progname);
+	exit(1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+	char	*fname;
+	FILE	*fp;
+	int	c;
+
+	init_charmap();
+
+	/* XXX */
+	yydebug = 1;
+
+	(void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)		/* Should be defined by cc -D */
+#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	while ((c = getopt(argc, argv, "clsf:t:?")) != EOF) {
+		switch (c) {
+		case 'c':
+			g_cflag = B_TRUE;
+			break;
+		case 'l':
+			g_lflag = B_TRUE;
+			break;
+		case 's':
+			g_sflag = B_TRUE;
+			break;
+		case 'f':
+			g_from_cs = optarg;
+			break;
+		case 't':
+			g_to_cs = optarg;
+			break;
+		case '?':
+			usage();
+		}
+	}
+
+	if (g_lflag) {
+		if (optind != argc)
+			usage();
+		list_codesets();
+		exit(0);
+	}
+
+	/* XXX form_cs not only codeset */
+	if (strstr(g_from_cs, "/") != NULL) {
+		reset_scanner(g_from_cs);
+		(void) yyparse();
+	}
+
+	/* XXX empty string "" current encoding */
+	if (g_from_cs == NULL) {
+		g_from_cs = nl_langinfo(CODESET);
+		printf("%s\n", g_from_cs);
+	}
+	if (g_to_cs == NULL)
+		g_to_cs = nl_langinfo(CODESET);
+
+	/*
+	 * XXX todo: deal with charmap files (/paths)
+	 */
+
+	g_ich = iconv_open(g_to_cs, g_from_cs);
+	if (g_ich == ((iconv_t)-1)) {
+		if (errno == EINVAL) {
+			(void) fprintf(stderr, gettext("Not supported %s to %s\n"),
+			    g_from_cs, g_to_cs);
+		} else {
+			(void) fprintf(stderr, "iconv_open failed\n");
+		}
+		exit(1);
+	}
+
+	if (optind == argc || (optind == argc - 1 &&
+	    0 == strcmp(argv[optind], "-"))) {
+		do_iconv(stdin, "stdin");
+		exit(0);
+	}
+
+	for (; optind < argc; optind++) {
+		fp = fopen(argv[optind], "r");
+		if (fp == NULL) {
+			perror(argv[optind]);
+			exit(1);
+		}
+		do_iconv(fp, argv[optind]);
+		(void) fclose(fp);
+	}
+
+	return (EXIT_SUCCESS);
+}
+
+/*
+ * Do actual conversion, copying *fp to stdout.
+ *
+ * Conversions may grow or shrink data, so using a larger output buffer
+ * to reduce the likelihood of leftover input buffer data in each pass.
+ */
+
+#define	IBUFSIZ	1024
+#define	OBUFSIZ	(2*IBUFSIZ)
+
+void
+do_iconv(FILE *fp, const char *fname)
+{
+	const char *iptr;
+	char	ibuf[IBUFSIZ];
+	char	obuf[OBUFSIZ];
+	char	*optr;
+	size_t	ileft, icnt, oleft, ocnt;
+	int	nr, nw, rc;
+
+	while ((nr = fread(ibuf, 1, IBUFSIZ, fp)) > 0) {
+
+		iptr = ibuf;
+		ileft = nr;
+
+		while (ileft > 0) {
+			optr = obuf;
+			oleft = OBUFSIZ;
+			rc = iconv(g_ich, &iptr, &ileft, &optr, &oleft);
+			if (rc == (size_t)-1) {
+				/*
+				 * XXX todo: deal with skipping invalid
+				 * input characters and continue...
+				 */
+				g_errcnt++;
+				break;
+			}
+			ocnt = OBUFSIZ - oleft;
+			nw = fwrite(obuf, 1, ocnt, stdout);
+			if (nw != ocnt) {
+				perror("write");
+				exit(1);
+			}
+		}
+	}
+
+	/*
+	 * End of file. Flush any shift encodings.
+	 */
+	iptr = NULL;
+	ileft = 0;
+	optr = obuf;
+	oleft = OBUFSIZ;
+	iconv(g_ich, &iptr, &ileft, &optr, &oleft);
+	ocnt = OBUFSIZ - oleft;
+	fwrite(obuf, 1, ocnt, stdout);
+}
+
+/*
+ * Item is in the list?
+ */
+static boolean_t
+iconv_find(list_t *list, const char *name)
+{
+	iconv_item_t	*head;
+	boolean_t	found = B_FALSE;
+
+	head = list_head(list);
+	while (head != NULL) {
+		if (strcmp(head->ii_name, name) == 0) {
+			found = B_TRUE;
+			break;
+		}
+		head = list_next(list, head);
+	}
+
+	return (found);
+}
+
+/*
+ * Insert into a sorted list.
+ */
+static void
+iconv_insert(list_t *list, const char *name)
+{
+	iconv_item_t	*head;
+	iconv_item_t	*item;
+
+	head = list_head(list);
+	while (head != NULL && strcmp(head->ii_name, name) < 0)
+		head = list_next(list, head);
+
+	item = (iconv_item_t *)malloc(sizeof (iconv_item_t));
+
+	list_link_init(&item->ii_next);
+	list_create(&item->ii_alias_list, sizeof (iconv_alias_t),
+	    offsetof(iconv_alias_t, ia_next));
+
+	item->ii_name = strdup(name);
+
+	list_insert_before(list, head, item);
+}
+
+static void
+iconv_insert_create(list_t *list, const char *name)
+{
+	if (!iconv_find(list, name))
+		iconv_insert(list, name);
+}
+
+static void
+iconv_print(list_t *list)
+{
+	iconv_item_t	*head;
+	iconv_alias_t	*alias_head;
+
+	(void) fprintf(stdout, gettext(
+	    "The following are all supported code set names.  All combinations\n"
+	    "of those names are not necessarily available for the pair of the\n"
+	    "fromcode-tocode.  Some of those code set names have aliases, which\n"
+	    "are case-insensitive and shown after the canonical name:\n"));
+
+	head = list_head(list);
+	while (head != NULL) {
+		(void) fprintf(stdout, "%s", head->ii_name);
+
+		if (!list_is_empty(&head->ii_alias_list)) {
+			printf(" (");
+			alias_head = list_head(&head->ii_alias_list);
+			while (alias_head != NULL) {
+				(void) fprintf(stdout, "%s",
+				    alias_head->ia_name);
+
+				alias_head = list_next(&head->ii_alias_list,
+				    alias_head);
+
+				if (alias_head != NULL)
+					(void) fprintf(stdout, ", ");
+			}
+			(void) fprintf(stdout, ")");
+		}
+
+		(void) fprintf(stdout, ",\n");
+
+		head = list_next(list, head);
+	}
+}
+
+/*
+ * List all codesets available.
+ */
+static void
+list_codesets(void)
+{
+	list_t	item_list;
+	glob_t	globbuf;
+	FILE	*fp;
+	char	*alias, *ptr, *chomp;
+	char	buf[1024];
+	int	i;
+
+	list_create(&item_list, sizeof (iconv_item_t),
+	    offsetof(iconv_item_t, ii_next));
+
+#define	_ICONV_PATH	"/usr/lib/iconv/"
+
+	/* XXX search path depends on arch amd64 etc */
+	(void) chdir(_ICONV_PATH);
+	(void) glob("*%*.so", GLOB_NOSORT, NULL, &globbuf);
+	(void) chdir("geniconvtbl/binarytables");
+	(void) glob("*%*.bt", GLOB_NOSORT|GLOB_APPEND, NULL, &globbuf);
+
+	for (i = 0; i < globbuf.gl_pathc; i++) {
+
+		ptr = globbuf.gl_pathv[i];
+		alias = strsep(&ptr, "%");
+
+		chomp = ptr;
+		for (; *chomp; chomp++) {
+			if (*chomp == '.')
+				*chomp = '\0';
+		}
+
+		iconv_insert_create(&item_list, ptr);
+		iconv_insert_create(&item_list, alias);
+	}
+
+	globfree(&globbuf);
+
+	(void) chdir(_ICONV_PATH);
+	(void) glob("*.*.t", GLOB_NOSORT, NULL, &globbuf);
+
+	for (i = 0; i < globbuf.gl_pathc; i++) {
+
+		ptr = globbuf.gl_pathv[i];
+		alias = strsep(&ptr, ".");
+printf("%s\n", ptr);
+		chomp = ptr;
+		for (; *chomp; chomp++) {
+			if (*chomp == '.')
+				*chomp = '\0';
+		}
+
+		iconv_insert_create(&item_list, ptr);
+		iconv_insert_create(&item_list, alias);
+	}
+
+	globfree(&globbuf);
+
+	/*
+	 * Read in the alias file and build up a list of
+	 * encoding aliases.
+	 */
+	fp = fopen("alias", "r");
+	if (fp == NULL) {
+		fprintf(stderr, gettext(
+		    "Failed to open the conversion alias file: %s\n"),
+		    "XXX");
+
+		/* XXX free list */
+		return;
+	}
+
+	while (fgets(buf, sizeof (buf), fp) != NULL) {
+		iconv_item_t *head;
+		iconv_alias_t *alias_head;
+
+		/* Skip comments */
+		if (buf[0] == '#')
+			continue;
+
+		ptr = buf;
+		alias = strsep(&ptr, " \t");
+
+		chomp = ptr;
+		for (; *chomp; chomp++) {
+			if (*chomp == '\n')
+				*chomp = '\0';
+		}
+
+		head = list_head(&item_list);
+		while (head != NULL &&
+		    strcmp(head->ii_name, ptr) < 0)
+			head = list_next(&item_list, head);
+
+		if (head != NULL) {
+			alias_head = (iconv_alias_t *)malloc(
+			    sizeof (iconv_alias_t));
+			list_link_init(&alias_head->ia_next);
+			alias_head->ia_name = strdup(alias);
+
+			list_insert_tail(&head->ii_alias_list, alias_head);
+		}
+	}
+
+	iconv_print(&item_list);
+
+	/* XXX free list */
+
+	(void) fclose(fp);
+}
