--- /dev/null	Sat May 28 21:32:39 2011
+++ new/usr/src/cmd/iconv/iconv.c	Sat May 28 21:32:39 2011
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ */
+
+/*
+ * iconv(1) command.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <iconv.h>
+#include <libintl.h>
+#include <langinfo.h>
+#include <locale.h>
+#include "charmap.h"
+
+#include <assert.h>
+
+const char *progname = "iconv";
+
+char *from_cs;
+char *to_cs;
+int debug;
+int cflag;	/* skip invalid characters */
+int sflag;	/* silent */
+int lflag;	/* list conversions */
+
+void iconv_file(FILE *, const char *);
+void list_codesets(void);
+
+iconv_t ich;	/* iconv(3c) lib handle */
+size_t (*pconv)(const char **iptr, size_t *ileft,
+		char **optr, size_t *oleft);
+
+size_t
+lib_iconv(const char **iptr, size_t *ileft, char **optr, size_t *oleft)
+{
+	return (iconv(ich, iptr, ileft, optr, oleft));
+}
+
+void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(
+	    "usage: %s [-cs] [-f from-codeset] [-t to-codeset] "
+	    "[file ...]\n"), progname);
+	(void) fprintf(stderr, gettext("\t%s -l\n"), progname);
+	exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+	FILE *fp;
+	char *fslash, *tslash;
+	int c;
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN	"SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	while ((c = getopt(argc, argv, "cdlsf:t:")) != EOF) {
+		switch (c) {
+		case 'c':
+			cflag++;
+			break;
+		case 'd':
+			debug++;
+			break;
+		case 'l':
+			lflag++;
+			break;
+		case 's':
+			sflag++;
+			break;
+		case 'f':
+			from_cs = optarg;
+			break;
+		case 't':
+			to_cs = optarg;
+			break;
+		case '?':
+			usage();
+		}
+	}
+
+	if (lflag) {
+		if (from_cs != NULL || to_cs != NULL || optind != argc)
+			usage();
+		list_codesets();
+		exit(0);
+	}
+
+	if (from_cs == NULL)
+		from_cs = nl_langinfo(CODESET);
+	if (to_cs == NULL)
+		to_cs = nl_langinfo(CODESET);
+
+	/*
+	 * If either "from" or "to" contains a slash,
+	 * then we're using charmaps.
+	 */
+	fslash = strchr(from_cs, '/');
+	tslash = strchr(to_cs, '/');
+	if (fslash != NULL || tslash != NULL) {
+		charmap_init(to_cs, from_cs);
+		pconv = cm_iconv;
+		if (debug)
+			charmap_dump();
+	} else {
+		ich = iconv_open(to_cs, from_cs);
+		if (ich == ((iconv_t)-1)) {
+			(void) fprintf(stderr, gettext("iconv_open failed\n"));
+			exit(1);
+		}
+		pconv = lib_iconv;
+	}
+
+	if (optind == argc || optind == argc - 1 &&
+	    0 == strcmp(argv[optind], "-")) {
+		iconv_file(stdin, "stdin");
+		exit(0);
+	}
+
+	for (; optind < argc; optind++) {
+		fp = fopen(argv[optind], "r");
+		if (fp == NULL) {
+			perror(argv[optind]);
+			exit(1);
+		}
+		iconv_file(fp, argv[optind]);
+		(void) fclose(fp);
+	}
+	exit(0);
+}
+
+/*
+ * Conversion buffer sizes:
+ *
+ * The input buffer has room to prepend one mbs character if needed for
+ * handling a left-over at the end of a previous conversion buffer.
+ *
+ * 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	(MB_LEN_MAX + BUFSIZ)
+#define	OBUFSIZ	(2 * BUFSIZ)
+
+void
+iconv_file(FILE *fp, const char *fname)
+{
+	static char ibuf[IBUFSIZ];
+	static char obuf[OBUFSIZ];
+	const char *iptr;
+	char *optr;
+	off64_t offset;
+	size_t ileft, oleft, ocnt;
+	int iconv_errno;
+	int nr, nw, rc;
+
+	offset = 0;
+	ileft = 0;
+	iptr = ibuf + MB_LEN_MAX;
+
+	while ((nr = fread(ibuf+MB_LEN_MAX, 1, BUFSIZ, fp)) > 0) {
+
+		assert(iptr <= ibuf+MB_LEN_MAX);
+		assert(ileft <= MB_LEN_MAX);
+		ileft += nr;
+		offset += nr;
+
+		optr = obuf;
+		oleft = OBUFSIZ;
+
+	iconv_again:
+		rc = (*pconv)(&iptr, &ileft, &optr, &oleft);
+		iconv_errno = errno;
+
+		ocnt = OBUFSIZ - oleft;
+		if (ocnt > 0) {
+			nw = fwrite(obuf, 1, ocnt, stdout);
+			if (nw != ocnt) {
+				perror("fwrite");
+				exit(1);
+			}
+		}
+		optr = obuf;
+		oleft = OBUFSIZ;
+
+		if (rc == (size_t)-1) {
+			switch (iconv_errno) {
+
+			case E2BIG:	/* no room in output buffer */
+				goto iconv_again;
+
+			case EINVAL:	/* incomplete sequence on input */
+				if (debug) {
+					(void) fprintf(stderr,
+			_("Incomplete sequence in %s at offset %lld\n"),
+					    fname, offset - ileft);
+				}
+				/*
+				 * Copy the reminder to the space reserved
+				 * at the start of the input buffer.
+				 */
+				assert(ileft > 0);
+				if (ileft <= MB_LEN_MAX) {
+					char *p = ibuf+MB_LEN_MAX-ileft;
+					(void) memcpy(p, iptr, ileft);
+					iptr = p;
+					continue; /* read again */
+				}
+				/*
+				 * Should not see ileft > MB_LEN_MAX,
+				 * but if we do, handle as EILSEQ.
+				 */
+				/* FALLTHROUGH */
+
+			case EILSEQ:	/* invalid sequence on input */
+				if (!sflag) {
+					(void) fprintf(stderr,
+			_("Illegal sequence in %s at offset %lld\n"),
+					    fname, offset - ileft);
+					(void) fprintf(stderr,
+			_("bad seq: \\x%02x\\x%02x\\x%02x\n"),
+					    iptr[0] & 0xff,
+					    iptr[1] & 0xff,
+					    iptr[2] & 0xff);
+				}
+				assert(ileft > 0);
+				/* skip one */
+				iptr++;
+				ileft--;
+				assert(oleft > 0);
+				if (!cflag) {
+					*optr++ = '?';
+					oleft--;
+				}
+				goto iconv_again;
+
+			default:
+				(void) fprintf(stderr,
+			_("iconv error (%s) in file $s at offset %lld\n"),
+				    strerror(errno), fname, offset - ileft);
+				perror("iconv");
+				break;
+			}
+		}
+
+		/* normal iconv return */
+		ileft = 0;
+		iptr = ibuf + MB_LEN_MAX;
+	}
+
+	/*
+	 * End of file
+	 * Flush any shift encodings.
+	 */
+	iptr = NULL;
+	ileft = 0;
+	optr = obuf;
+	oleft = OBUFSIZ;
+	(*pconv)(&iptr, &ileft, &optr, &oleft);
+	ocnt = OBUFSIZ - oleft;
+	if (ocnt > 0) {
+		(void) fwrite(obuf, 1, ocnt, stdout);
+	}
+}
+
+/*
+ * scan the /usr/lib/iconv directory...
+ * A script for this seems appropriate.
+ */
+void
+list_codesets(void)
+{
+	(void) system("/usr/lib/iconv/iconv_list");
+}
