summaryrefslogtreecommitdiff
path: root/src/iconv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iconv.c')
-rw-r--r--src/iconv.c110
1 files changed, 110 insertions, 0 deletions
diff --git a/src/iconv.c b/src/iconv.c
new file mode 100644
index 0000000..a0938d6
--- /dev/null
+++ b/src/iconv.c
@@ -0,0 +1,110 @@
+/*
+ * iconv.c
+ * Implementation of SUSv4 XCU iconv utility
+ * Copyright © 2011 Rich Felker
+ * Licensed under the terms of the GNU General Public License, v2 or later
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <iconv.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+int main(int argc, char **argv)
+{
+ const char *from=0, *to=0;
+ int b;
+ iconv_t cd;
+ char buf[BUFSIZ];
+ char outbuf[BUFSIZ*4];
+ char *in, *out;
+ size_t inb;
+ size_t l;
+ size_t unitsize=0;
+ int err=0;
+ FILE *f;
+
+ while ((b = getopt(argc, argv, "f:t:csl")) != EOF) switch(b) {
+ case 'l':
+ puts("UTF-8, UTF-16BE, UTF-16LE, UTF-32BE, UTF32-LE, UCS-2BE, UCS-2LE, WCHAR_T,\n"
+ "US_ASCII, ISO8859-1, ISO8859-2, ISO8859-3, ISO8859-4, ISO8859-5,\n"
+ "ISO8859-6, ISO8859-7, ...");
+ exit(0);
+ case 'c': case 's': break;
+ case 'f': from=optarg; break;
+ case 't': to=optarg; break;
+ default: exit(1);
+ }
+
+ if (!from || !to) {
+ setlocale(LC_CTYPE, "");
+ if (!to) to = nl_langinfo(CODESET);
+ if (!from) from = nl_langinfo(CODESET);
+ }
+ cd = iconv_open(to, from);
+ if (cd == (iconv_t)-1) {
+ if (iconv_open(to, "WCHAR_T") == (iconv_t)-1)
+ fprintf(stderr, "iconv: destination charset %s", to);
+ else
+ fprintf(stderr, "iconv: source charset %s", from);
+ perror("");
+ exit(1);
+ }
+ if (optind == argc) argv[argc++] = "-";
+
+ for (; optind < argc; optind++) {
+ if (argv[optind][0]=='-' && !argv[optind][1]) {
+ f = stdin;
+ argv[optind] = "(stdin)";
+ } else if (!(f = fopen(argv[optind], "rb"))) {
+ fprintf(stderr, "iconv: %s", argv[optind]);
+ perror("");
+ err = 1;
+ continue;
+ }
+ inb = 0;
+ for (;;) {
+ in = buf;
+ out = outbuf;
+ l = fread(buf+inb, 1, sizeof(buf)-inb, f);
+ inb += l;
+ if (!inb) break;
+ if (iconv(cd, &in, &inb, &out, (size_t [1]){sizeof outbuf})==-1
+ && errno == EILSEQ) {
+ if (!unitsize) {
+ wchar_t wc='0';
+ char dummy[4], *dummyp=dummy;
+ iconv_t cd2 = iconv_open(from, "WCHAR_T");
+ if (cd == (iconv_t)-1) {
+ unitsize = 1;
+ } else {
+ iconv(cd2,
+ (char *[1]){(char *)&wc},
+ (size_t[1]){1},
+ &dummyp, (size_t[1]){4});
+ unitsize = dummyp-dummy;
+ if (!unitsize) unitsize=1;
+ }
+ }
+ inb-=unitsize;
+ in+=unitsize;
+ }
+ if (inb && !l && errno==EINVAL) break;
+ if (out>outbuf && !fwrite(outbuf, out-outbuf, 1, stdout)) {
+ perror("iconv: write error");
+ exit(1);
+ }
+ if (inb) memmove(buf, in, inb);
+ }
+ if (ferror(f)) {
+ fprintf(stderr, "iconv: %s", argv[optind]);
+ perror("");
+ err = 1;
+ }
+ }
+ return err;
+}