--- old/usr/src/cmd/Makefile	2013-02-24 04:11:46.953023766 +0000
+++ new/usr/src/cmd/Makefile	2013-02-24 04:11:46.965996919 +0000
@@ -197,6 +197,7 @@
 	hotplugd	\
 	hwdata		\
 	ibd_upgrade	\
+	iconv		\
 	id		\
 	idmap		\
 	infocmp		\
--- /dev/null	2013-02-24 04:11:47.000000000 +0000
+++ new/usr/src/cmd/iconv/Makefile	2013-02-24 04:11:47.416444947 +0000
@@ -0,0 +1,60 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License").  You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= iconv
+
+include ../Makefile.cmd
+
+OBJS	= charmap.o iconv.o parser.tab.o scanner.o
+
+.KEEP_STATE:
+
+CFLAGS +=	$(CCVERBOSE)
+
+LDLIBS += -lcmdutils
+LDLIBS += -lavl
+YFLAGS	= -d -b parser
+
+CLEANFILES	= $(OBJS) parser.tab.c parser.tab.h
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+$(PROG):	$(OBJS)
+	$(LINK.C) $(CFLAGS) $(OBJS) -o $@ $(LDLIBS)
+
+$(OBJS):	parser.tab.h
+
+parser.tab.c parser.tab.h: parser.y
+	$(YACC) $(YFLAGS) parser.y
+
+clean:
+	$(RM) $(CLEANFILES)
+
+lint:	lint_PROG
+
+include ../Makefile.targ
--- /dev/null	2013-02-24 04:11:47.000000000 +0000
+++ new/usr/src/cmd/iconv/charmap.c	2013-02-24 04:11:47.644924595 +0000
@@ -0,0 +1,147 @@
+/*
+ * 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 2010 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2013 David Hoeppner.  All rights reserved.
+ */
+
+/*
+ * Character map handling for iconv.
+ */
+
+#include <sys/avl.h>
+
+#include <stddef.h>
+
+#include "iconv.h"
+#include "parser.tab.h"
+
+/*
+ * AVL trees for the from and to charmaps.
+ */
+static avl_tree_t	from_cmap_sym;
+static avl_tree_t	from_cmap_wc;
+static avl_tree_t	to_cmap_sym;
+static avl_tree_t	to_cmap_wc;
+
+static avl_tree_t	*current_cmap_sym = &from_cmap_sym;
+static avl_tree_t	*current_cmap_wc = &from_cmap_wc;
+
+typedef struct charmap {
+	const char	*name;
+	wchar_t		wc;
+	avl_node_t	avl_sym;
+	avl_node_t	avl_wc;
+} charmap_t;
+
+static int
+cmap_compare_sym(const void *n1, const void *n2)
+{
+	const charmap_t	*c1 = n1;
+	const charmap_t	*c2 = n2;
+	int	rv;
+
+	rv = strcmp(c1->name, c2->name);
+	return ((rv < 0) ? -1 : (rv > 0) ? 1 : 0);
+}
+
+static int
+cmap_compare_wc(const void *n1, const void *n2)
+{
+	const charmap_t	*c1 = n1;
+	const charmap_t	*c2 = n2;
+
+	return ((c1->wc < c2->wc) ? -1 : (c1->wc > c2->wc) ? 1 : 0);
+}
+
+void
+init_charmap(void)
+{
+	avl_create(&from_cmap_sym, cmap_compare_sym, sizeof (charmap_t),
+	    offsetof(charmap_t, avl_sym));
+
+	avl_create(&from_cmap_wc, cmap_compare_wc, sizeof (charmap_t),
+	    offsetof(charmap_t, avl_wc));
+
+	avl_create(&to_cmap_sym, cmap_compare_sym, sizeof (charmap_t),
+	    offsetof(charmap_t, avl_sym));
+
+	avl_create(&to_cmap_wc, cmap_compare_wc, sizeof (charmap_t),
+	    offsetof(charmap_t, avl_wc));
+}
+
+/*
+ * Switches from fromcharmap to tocharmap.
+ */
+void
+switch_charmap(void)
+{
+	current_cmap_sym = &to_cmap_sym;
+	current_cmap_wc = &to_cmap_wc;
+}
+
+static void
+add_charmap_impl(char *sym, wchar_t wc, int nodups)
+{
+	charmap_t	srch;
+	charmap_t	*n = NULL;
+	avl_index_t	where;
+
+	srch.wc = wc;
+	srch.name = sym;
+
+	/*
+	 * Also possibly insert the wide mapping, although note that there
+	 * can only be one of these per wide character code.
+	 */
+	if ((wc != -1) && ((avl_find(current_cmap_wc, &srch, &where)) == NULL)) {
+		if ((n = calloc(1, sizeof (*n))) == NULL) {
+			errf(_("out of memory"));
+			return;
+		}
+
+		n->wc = wc;
+		avl_insert(current_cmap_wc, n, where);
+	}
+
+	if (sym != NULL) {
+		if (avl_find(current_cmap_sym, &srch, &where) != NULL) {
+			if (nodups == 1) {
+				errf(_("duplicate character definition"));
+			}
+
+			return;
+		}
+
+		if ((n == NULL) && ((n = calloc(1, sizeof (*n))) == NULL)) {
+			errf(_("out of memory"));
+			return;
+		}
+
+		n->wc = wc;
+		n->name = sym;
+printf("ADDING %s\n", sym);
+		avl_insert(current_cmap_sym, n, where);
+	}
+}
+
+void
+add_charmap(char *sym, int c)
+{
+	add_charmap_impl(sym, c, 1);
+}
+
+void
+add_charmap_range(char *s, char *e, int wc)
+{
+
+}
--- /dev/null	2013-02-24 04:11:47.000000000 +0000
+++ new/usr/src/cmd/iconv/iconv.c	2013-02-24 04:11:47.854217718 +0000
@@ -0,0 +1,446 @@
+/*
+ * 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)
+{
+	(void) fprintf(stderr, _(
+	    "usage:"
+	    "\ticonv [-cs] [-f fromcode] [-t tocode] [file ...]\n"
+	    "\ticonv [-cs] -f frommap -t tomap [file ...]\n"
+	    "\ticonv -l\n"));
+	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);
+	}
+
+	/* Charmaps and codesets can't be mixed */
+	if ((strchr(g_from_cs, '/') == NULL) !=
+	    (strchr(g_to_cs, '/') == NULL)) {
+		usage();
+	}
+
+	/* XXX form_cs not only codeset */
+	if (strchr(g_from_cs, '/') != NULL) {
+		reset_scanner(g_from_cs);
+		(void) yyparse();
+
+		switch_charmap();
+
+		reset_scanner(g_to_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);
+}
--- /dev/null	2013-02-24 04:11:48.000000000 +0000
+++ new/usr/src/cmd/iconv/iconv.h	2013-02-24 04:11:48.083011156 +0000
@@ -0,0 +1,59 @@
+/*
+ * 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 is of the CDDL is also available via the Internet
+ * at http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2013 David Hoeppner.  All rights reserved.
+ */
+
+/*
+ * POSIX iconv.
+ */
+
+#include <sys/types.h>
+
+#include <libintl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+/*
+ * Macros.
+ */
+#define	_(x)	gettext(x)
+
+extern int	com_char;	/* Comment character */
+extern int	esc_char;	/* Escape character */
+extern int	mb_cur_max;
+extern int	mb_cur_min;
+
+extern int	yydebug;
+extern int	lineno;
+
+/*
+ * Functions from scanner.c
+ */
+char	*to_mb_string(const wchar_t *);
+void	set_wide_encoding(const char *);
+void	add_wcs(wchar_t);
+wchar_t	*get_wcs(void);
+void	reset_scanner(const char *);
+void	yyerror(const char *);
+void	errf(const char *, ...);
+void	scan_to_eol(void);
+
+/*
+ * Functions from charmap.c
+ */
+void	init_charmap(void);
+void	switch_charmap(void);
+void	add_charmap(char *, int);
+void	add_charmap_range(char *, char *, int);
--- /dev/null	2013-02-24 04:11:48.000000000 +0000
+++ new/usr/src/cmd/iconv/parser.y	2013-02-24 04:11:48.282334223 +0000
@@ -0,0 +1,156 @@
+%{
+/*
+ * 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 2010 Nexenta Systems, Inc.  All rights reserved.
+ */
+
+/*
+ * POSIX charmap grammar.
+ */
+
+#include <wchar.h>
+#include <stdio.h>
+#include <limits.h>
+#include "iconv.h"
+
+%}
+%union {
+	int		num;
+	wchar_t		wc;
+	char		*token;
+}
+
+%token		T_CODE_SET
+%token		T_MB_CUR_MAX
+%token		T_MB_CUR_MIN
+%token		T_COM_CHAR
+%token		T_ESC_CHAR
+%token		T_LT
+%token		T_GT
+%token		T_NL
+%token		T_SEMI
+%token		T_COMMA
+%token		T_ELLIPSIS
+%token		T_RPAREN
+%token		T_LPAREN
+%token		T_QUOTE
+%token		T_NULL
+%token		T_WS
+%token		T_END
+%token		T_COPY
+%token		T_CHARMAP
+%token		T_WIDTH
+%token		T_WIDTH_DEFAULT
+%token	<wc>		T_CHAR
+%token	<token>		T_NAME
+%token	<num>		T_NUMBER
+%token	<token>		T_SYMBOL
+
+%%
+
+iconv		: setting_list categories
+		| categories
+		;
+
+
+setting_list	: setting_list setting
+		| setting
+		;
+
+setting		: T_COM_CHAR T_CHAR T_NL
+		{
+			printf("SEtting comment\n");
+			com_char = $2;
+		}
+		| T_ESC_CHAR T_CHAR T_NL
+		{
+			esc_char = $2;
+		}
+		| T_MB_CUR_MAX T_NUMBER T_NL
+		{
+			mb_cur_max = $2;
+		}
+		| T_MB_CUR_MIN T_NUMBER T_NL
+		{
+			mb_cur_min = $2;
+		}
+		| T_CODE_SET string T_NL
+		{
+			wchar_t	*w = get_wcs();
+
+			set_wide_encoding(to_mb_string(w));
+			free(w);
+		}
+		| T_CODE_SET T_NAME T_NL
+		{
+			set_wide_encoding($2);
+		}
+		;
+
+categories	: categories category
+		| category
+		;
+
+category	: charmap
+		| width
+		;
+
+charmap		: T_CHARMAP T_NL charmap_list T_END T_CHARMAP T_NL
+
+charmap_list	: charmap_list charmap_entry
+		| charmap_entry
+		;
+
+charmap_entry	: T_SYMBOL T_CHAR
+		{
+			add_charmap($1, $2);
+			scan_to_eol();
+		}
+		| T_SYMBOL T_ELLIPSIS T_SYMBOL T_CHAR
+		{
+			add_charmap_range($1, $3, $4);
+			scan_to_eol();
+		}
+		| T_NL
+		;
+
+width		: T_WIDTH T_NL width_list T_END T_WIDTH T_NL
+
+width_list	: width_list width_entry
+		| width_entry
+		;
+
+width_entry	: T_SYMBOL T_NUMBER
+		{
+			printf("WIDTH ENTRY\n");
+		}
+		| T_SYMBOL T_ELLIPSIS T_SYMBOL T_NUMBER
+		{
+			printf("WIDTH ENTY ELL\n");
+		}
+		| T_NL
+		;
+
+string		: T_QUOTE charlist T_QUOTE
+		| T_QUOTE T_QUOTE
+		;
+
+charlist	: charlist T_CHAR
+		{
+			add_wcs($2);
+		}
+		| T_CHAR
+		{
+			add_wcs($1);
+		}
+		;
--- /dev/null	2013-02-24 04:11:48.000000000 +0000
+++ new/usr/src/cmd/iconv/scanner.c	2013-02-24 04:11:48.491431527 +0000
@@ -0,0 +1,728 @@
+/*
+ * 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 2010 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2013 David Hoeppner.  All rights reserved.
+ */
+
+/*
+ * Functions to charmap .
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <widec.h>
+
+#include "iconv.h"
+#include "parser.tab.h"
+
+/*
+ * Helper macros.
+ */
+#define	hex(x)	\
+	(isdigit(x) ? (x - '0') : ((islower(x) ? (x - 'a') : (x - 'A')) + 10))
+
+#define	isodigit(x)	((x >= '0') && (x <= '7'))
+
+/*
+ * Charmap specific.
+ */
+int		com_char = '#';
+int		esc_char = '\\';
+int		mb_cur_max = 1;
+int		mb_cur_min = 1;
+
+int		lineno = 1;
+static FILE	*input = stdin;
+static const char *filename = "<stdin>";
+static int	escaped = 0;
+static int	instring = 0;
+static int	nextline;
+
+/*
+ * Tokens.
+ */
+static char	*token = NULL;
+static int	tokidx;
+static int	toksz = 0;
+static int	hadtok = 0;
+
+/*
+ * Wide strings.
+ */
+static wchar_t	*widestr = NULL;
+static int	wideidx = 0;
+static int	widesz = 0;
+
+/*
+ * Keywords related.
+ */
+int		last_kw = 0;
+static int	category = T_END;
+
+static struct token {
+	int	id;
+	const char *name;
+} keywords[] = {
+	{ T_COM_CHAR,		"comment_char" },
+	{ T_ESC_CHAR,		"escape_char" },
+	{ T_END,		"END" },
+	{ T_CHARMAP,		"CHARMAP" },
+	{ T_WIDTH,		"WIDTH" },
+	{ T_WIDTH_DEFAULT,	"WIDTH_DEFAULT" },
+	{ -1, NULL },
+};
+
+/*
+ * Charmap reserved keywords.
+ */
+static struct token symwords[] = {
+	{ T_COM_CHAR,		"comment_char" },
+	{ T_ESC_CHAR,		"escape_char" },
+	{ T_CODE_SET,		"code_set_name" },
+	{ T_MB_CUR_MAX,		"mb_cur_max" },
+	{ T_MB_CUR_MIN,		"mb_cur_min" },
+	{ -1, NULL },
+};
+
+static int categories[] = {
+	T_CHARMAP,
+	T_WIDTH,
+	0,
+};
+
+char *
+to_mb_string(const wchar_t *wcs)
+{
+	return (NULL);
+}
+
+void
+set_wide_encoding(const char *encoding)
+{
+}
+
+/*
+ * Reset the scanner variables and open the supplied charmap file.
+ */
+void
+reset_scanner(const char *fname)
+{
+	input = fopen(fname, "r");
+	if (input == NULL) {
+		perror("fopen");
+		exit(4);
+	}
+
+	filename = fname;
+	com_char = '#';
+	esc_char = '\\';
+	instring = 0;
+	escaped = 0;
+	lineno = 1;
+	nextline = 1;
+	tokidx = 0;
+	wideidx = 0;
+}
+
+static int
+scanc(void)
+{
+	int	c;
+
+	c = getc(input);
+	lineno = nextline;
+	if (c == '\n') {
+		nextline++;
+	}
+
+	return (c);
+}
+
+static void
+unscanc(int c)
+{
+	if (c == '\n') {
+		nextline--;
+	}
+
+	if (ungetc(c, input) < 0) {
+		yyerror(_("ungetc failed"));
+	}
+}
+
+static int
+scan_hex_byte(void)
+{
+	int	c1, c2;
+	int	v;
+
+	c1 = scanc();
+	if (!isxdigit(c1)) {
+		yyerror(_("malformed hex digit"));
+		return (0);
+	}
+	c2 = scanc();
+	if (!isxdigit(c2)) {
+		yyerror(_("malformed hex digit"));
+		return (0);
+	}
+	v = ((hex(c1) << 4) | hex(c2));
+	return (v);
+}
+
+static int
+scan_dec_byte(void)
+{
+	int	c1, c2, c3;
+	int	b;
+
+	c1 = scanc();
+	if (!isdigit(c1)) {
+		yyerror(_("malformed decimal digit"));
+		return (0);
+	}
+	b = c1 - '0';
+	c2 = scanc();
+	if (!isdigit(c2)) {
+		yyerror(_("malformed decimal digit"));
+		return (0);
+	}
+	b *= 10;
+	b += (c2 - '0');
+	c3 = scanc();
+	if (!isdigit(c3)) {
+		unscanc(c3);
+	} else {
+		b *= 10;
+		b += (c3 - '0');
+	}
+	return (b);
+}
+
+static int
+scan_oct_byte(void)
+{
+	int	c1, c2, c3;
+	int	b;
+
+	b = 0;
+
+	c1 = scanc();
+	if (!isodigit(c1)) {
+		yyerror(_("malformed octal digit"));
+		return (0);
+	}
+	b = c1 - '0';
+	c2 = scanc();
+	if (!isodigit(c2)) {
+		yyerror(_("malformed octal digit"));
+		return (0);
+	}
+	b *= 8;
+	b += (c2 - '0');
+	c3 = scanc();
+	if (!isodigit(c3)) {
+		unscanc(c3);
+	} else {
+		b *= 8;
+		b += (c3 - '0');
+	}
+	return (b);
+}
+
+void
+add_tok(int c)
+{
+	if ((tokidx + 1) >= toksz) {
+		toksz += 64;
+
+		if ((token = realloc(token, toksz)) == NULL) {
+			yyerror(_("out of memory"));
+			tokidx = 0;
+			toksz = 0;
+			return;
+		}
+	}
+
+	token[tokidx++] = (char)c;
+	token[tokidx] = 0;
+}
+
+void
+add_wcs(wchar_t c)
+{
+	if ((wideidx + 1) >= widesz) {
+		widesz += 64;
+		widestr = realloc(widestr, (widesz * sizeof (wchar_t)));
+		if (widestr == NULL) {
+			yyerror(_("out of memory"));
+			wideidx = 0;
+			widesz = 0;
+			return;
+		}
+	}
+
+	widestr[wideidx++] = c;
+	widestr[wideidx] = 0;
+}
+
+wchar_t *
+get_wcs(void)
+{
+	wchar_t	*ws = widestr;
+
+	wideidx = 0;
+	widestr = NULL;
+	widesz = 0;
+
+	if (ws == NULL) {
+		if ((ws = wsdup(L"")) == NULL) {
+			yyerror(_("out of memory"));
+		}
+	}
+
+	return (ws);
+}
+
+static int
+get_byte(void)
+{
+	int	c;
+
+	if ((c = scanc()) != esc_char) {
+		unscanc(c);
+		return (EOF);
+	}
+
+	c = scanc();
+
+	switch (c) {
+	case 'd':
+	case 'D':
+		return (scan_dec_byte());
+	case 'x':
+	case 'X':
+		return (scan_hex_byte());
+	case '0' ... '7':
+		/* Put the character back so we can get it */
+		unscanc(c);
+		return (scan_oct_byte());
+	default:
+		unscanc(c);
+		unscanc(esc_char);
+		return (EOF);
+	}
+}
+
+int
+get_escaped(int c)
+{
+	switch (c) {
+	case 'n':
+		return ('\n');
+	case 'r':
+		return ('\r');
+	case 't':
+		return ('\t');
+	case 'f':
+		return ('\f');
+	case 'v':
+		return ('\v');
+	case 'b':
+		return ('\b');
+	case 'a':
+		return ('\a');
+	default:
+		return (c);
+	}
+}
+
+int
+get_wide(void)
+{
+	char	mbs[MB_LEN_MAX + 1] = "";
+	int	mbi = 0;
+	int	c;
+	wchar_t	wc;
+
+	if (mb_cur_max >= sizeof (mbs)) {
+		yyerror(_("max multibyte character size too big"));
+		mbi = 0;
+		return (T_NULL);
+	}
+
+	for (;;) {
+		if ((mbi == mb_cur_max) || ((c = get_byte()) == EOF)) {
+			/*
+			 * End of the byte sequence reached, but no
+			 * valid wide decoding.  Fatal error.
+			 */
+			mbi = 0;
+			yyerror(_("not a valid character encoding"));
+			return (T_NULL);
+		}
+
+		mbs[mbi++] = c;
+		mbs[mbi] = 0;
+
+		if (mbi == mb_cur_max) {
+			break;
+		}
+	}
+
+	mbi = 0;
+	/* XXX */
+	yylval.wc = (uint8_t)*mbs;
+
+	return (T_CHAR);
+}
+
+int
+get_symbol(void)
+{
+	int	c;
+
+	while ((c = scanc()) != EOF) {
+		if (escaped == 1) {
+			escaped = 0;
+			if (c == '\n') {
+				continue;
+			}
+
+			add_tok(get_escaped(c));
+			continue;
+		}
+
+		if (c == esc_char) {
+			escaped = 1;
+			continue;
+		}
+
+		if (c == '\n') {	/* Well that's strange! */
+			yyerror(_("unterminated symbolic name"));
+			continue;
+		}
+
+		if (c == '>') {		/* End of symbol */
+			/*
+			 * This restarts the token from the beginning
+			 * the next time we scan a character.  (This
+			 * token is complete.)
+			 */
+			if (token == NULL) {
+				yyerror(_("missing symbolic name"));
+				return (T_NULL);
+			}
+
+			tokidx = 0;
+
+			/*
+			 * A few symbols are handled as keywords outside
+			 * of the normal categories.
+			 */
+			if (category == T_END) {
+				int	i;
+
+				for (i = 0; symwords[i].name != 0; i++) {
+					if (strcmp(token, symwords[i].name) ==
+					    0) {
+						last_kw = symwords[i].id;
+						return (last_kw);
+					}
+				}
+			}
+
+			/* XXX */
+
+			/* Its an undefined symbol */
+			yylval.token = strdup(token);
+			token = NULL;
+			toksz = 0;
+			tokidx = 0;
+printf("returning SYMBOL %s\n", yylval.token);
+			return (T_SYMBOL);
+		}
+
+		add_tok(c);
+	}
+
+	yyerror(_("unterminated symbolic name"));
+
+	return (EOF);
+}
+
+static int
+consume_token(void)
+{
+	int	len = tokidx;
+	int	i;
+
+	tokidx = 0;
+	if (token == NULL) {
+		return (T_NULL);
+	}
+
+	/*
+	 * This one is special, because we don't want it to alter the
+	 * last_kw field.
+	 */
+	if (strcmp(token, "...") == 0) {
+		return (T_ELLIPSIS);
+	}
+
+	/* Search for reserved words first */
+	for (i = 0; keywords[i].name; i++) {
+		int	j;
+
+		if (strcmp(keywords[i].name, token)) {
+			continue;
+		}
+
+		last_kw = keywords[i].id;
+
+		/* Clear the top level category if we're done with it */
+		if (last_kw == T_END) {
+			category = T_END;
+		}
+
+		/* Set the top level category if we're changing */
+		for (j = 0; categories[j]; j++) {
+			if (categories[j] != last_kw) {
+				continue;
+			}
+			category = last_kw;
+		}
+
+		return (keywords[i].id);
+	}
+
+	/* Maybe its a numeric constant? */
+	if (isdigit(*token) || (*token == '-' && isdigit(token[1]))) {
+		char	*eptr;
+
+		yylval.num = strtol(token, &eptr, 10);
+		if (*eptr != 0) {
+			yyerror(_("malformed number"));
+		}
+
+		return (T_NUMBER);
+	}
+
+	/*
+	 * A single lone character is treated as a character literal.
+	 * To avoid duplication of effort, we stick in the charmap.
+	 */
+	if (len == 1) {
+		yylval.wc = token[0];
+		return (T_CHAR);
+	}
+
+	/* Anything else is treated as a symbolic name */
+	yylval.token = strdup(token);
+	token = NULL;
+	toksz = 0;
+	tokidx = 0;
+
+	return (T_NAME);
+}
+
+void
+scan_to_eol(void)
+{
+	int	c;
+
+	while ((c = scanc()) != '\n') {
+		if (c == EOF) {
+			/* end of file without newline! */
+			errf(_("missing newline"));
+			return;
+		}
+	}
+
+	assert(c == '\n');
+}
+
+int
+yylex(void)
+{
+	int	c;
+
+	while ((c = scanc()) != EOF) {
+printf("--- yylex --%c--\n", c);
+
+		/* Special handling for quoted strings */
+		if (instring == 1) {
+			if (escaped == 1) {
+				escaped = 0;
+
+				/* If newline, just eat and forget it */
+				if (c == '\n') {
+					continue;
+				}
+
+				if (strchr("xd01234567", c)) {
+					unscanc(c);
+					unscanc(esc_char);
+					return (get_wide());
+				}
+
+				yylval.wc = get_escaped(c);
+				return (T_CHAR);
+			}
+
+			if (c == esc_char) {
+				escaped = 1;
+				continue;
+			}
+
+			switch (c) {
+			case '<':
+				return (get_symbol());
+			case '>':
+				/* Opps! Should generate syntax error */
+				return (T_GT);
+			case '"':
+				instring = 0;
+				return (T_QUOTE);
+			default:
+				yylval.wc = c;
+				return (T_CHAR);
+			}
+		}
+
+		/* Escaped characters first */
+		if (escaped == 1) {
+			escaped = 0;
+			if (c == '\n') {
+				/* Eat the newline */
+				continue;
+			}
+			hadtok = 1;
+			if (tokidx != 0) {
+				/* An escape mid-token is nonsense */
+				return (T_NULL);
+			}
+
+			/* Numeric escapes are treated as wide characters */
+			if (strchr("xXd01234567", c)) {
+				unscanc(c);
+				unscanc(esc_char);
+				return (get_wide());
+			}
+
+			add_tok(get_escaped(c));
+			continue;
+		}
+
+		/* If it is the escape character itself note it */
+		if (c == esc_char) {
+			escaped = 1;
+			continue;
+		}
+
+		/* Remove from the comment character to end of line */
+		if (c == com_char) {
+			while (c != '\n') {
+				if ((c = scanc()) == EOF) {
+					/* End of file without newline */
+					return (EOF);
+				}
+			}
+
+			assert(c == '\n');
+
+			if (hadtok == 0) {
+				/*
+				 * If there were no tokens on this line,
+				 * then just pretend it didn't exist at all.
+				 */
+				continue;
+			}
+
+			hadtok = 0;
+			return (T_NL);
+		}
+
+		if (strchr(" \t\n;()<>,\"", c) && (tokidx != 0)) {
+			/*
+			 * These are all token delimiters.  If there
+			 * is a token already in progress, we need to
+			 * process it.
+			 */
+			unscanc(c);
+			return (consume_token());
+		}
+
+		switch (c) {
+		case '\n':
+			if (hadtok == 0) {
+				/*
+				 * If the line was completely devoid of tokens,
+				 * then just ignore it.
+				 */
+				continue;
+			}
+
+			/* We're starting a new line, reset the token state */
+			hadtok = 0;
+			return (T_NL);
+		case '>':
+			hadtok = 1;
+			return (T_GT);
+		case '<':
+			/* Symbol start! */
+			hadtok = 1;
+			return (get_symbol());
+		case ' ':
+		case '\t':
+			/* Whitespace, just ignore */
+			continue;
+		case '"':
+			hadtok = 1;
+			instring = 1;
+			return (T_QUOTE);
+		default:
+//printf("--- adding %c to token\n", c);
+			hadtok = 1;
+			add_tok(c);
+			continue;
+		}
+	}
+
+	return (EOF);
+}
+
+void
+yyerror(const char *msg)
+{
+	(void) fprintf(stderr, _("%s: %d: error: %s\n"),
+	    filename, lineno, msg);
+	exit(4);
+}
+
+void
+errf(const char *fmt, ...)
+{
+	char	*msg;
+	va_list	va;
+
+	va_start(va, fmt);
+	(void) vasprintf(&msg, fmt, va);
+	va_end(va);
+
+	(void) fprintf(stderr, _("%s: %d: error: %s\n"),
+	    filename, lineno, msg);
+	free(msg);
+	exit(4);
+}
