1/* $NetBSD: iconv.c,v 1.17 2011/09/16 15:39:26 joerg Exp $ */ 2 3/*- 4 * Copyright (c)2003 Citrus Project, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30#if defined(LIBC_SCCS) && !defined(lint) 31__RCSID("$NetBSD: iconv.c,v 1.17 2011/09/16 15:39:26 joerg Exp $"); 32#endif /* LIBC_SCCS and not lint */ 33 34#include <err.h> 35#include <errno.h> 36#include <iconv.h> 37#include <langinfo.h> 38#include <locale.h> 39#include <stdio.h> 40#include <stdlib.h> 41#include <string.h> 42#include <unistd.h> 43#include <util.h> 44 45static void usage(void) __dead; 46static int scmp(const void *, const void *); 47static void show_codesets(void); 48static void do_conv(const char *, FILE *, const char *, const char *, int, int); 49 50static void 51usage(void) 52{ 53 (void)fprintf(stderr, 54 "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n" 55 "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n" 56 "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n" 57 "\t%1$s -l\n", getprogname()); 58 exit(1); 59} 60 61/* 62 * qsort() helper function 63 */ 64static int 65scmp(const void *v1, const void *v2) 66{ 67 const char * const *s1 = v1; 68 const char * const *s2 = v2; 69 70 return strcasecmp(*s1, *s2); 71} 72 73static void 74show_codesets(void) 75{ 76 char **list; 77 size_t sz, i; 78 79 if (__iconv_get_list(&list, &sz)) 80 err(EXIT_FAILURE, "__iconv_get_list()"); 81 82 qsort(list, sz, sizeof(char *), scmp); 83 84 for (i = 0; i < sz; i++) 85 (void)printf("%s\n", list[i]); 86 87 __iconv_free_list(list, sz); 88} 89 90#define INBUFSIZE 1024 91#define OUTBUFSIZE (INBUFSIZE * 2) 92/*ARGSUSED*/ 93static void 94do_conv(const char *fn, FILE *fp, const char *from, const char *to, int silent, 95 int hide_invalid) 96{ 97 char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *out; 98 const char *in; 99 size_t inbytes, outbytes, ret, invalids; 100 iconv_t cd; 101 uint32_t flags = 0; 102 103 if (hide_invalid) 104 flags |= __ICONV_F_HIDE_INVALID; 105 cd = iconv_open(to, from); 106 if (cd == (iconv_t)-1) 107 err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 108 109 invalids = 0; 110 while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { 111 in = inbuf; 112 while (inbytes > 0) { 113 size_t inval; 114 115 out = outbuf; 116 outbytes = OUTBUFSIZE; 117 ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 118 flags, &inval); 119 invalids += inval; 120 if (outbytes < OUTBUFSIZE) 121 (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, 122 stdout); 123 if (ret == (size_t)-1 && errno != E2BIG) { 124 /* 125 * XXX: iconv(3) is bad interface. 126 * invalid character count is lost here. 127 * instead, we just provide __iconv function. 128 */ 129 if (errno != EINVAL || in == inbuf) 130 err(EXIT_FAILURE, "iconv()"); 131 132 /* incomplete input character */ 133 (void)memmove(inbuf, in, inbytes); 134 ret = fread(inbuf + inbytes, 1, 135 INBUFSIZE - inbytes, fp); 136 if (ret == 0) { 137 fflush(stdout); 138 if (feof(fp)) 139 errx(EXIT_FAILURE, 140 "unexpected end of file; " 141 "the last character is " 142 "incomplete."); 143 else 144 err(EXIT_FAILURE, "fread()"); 145 } 146 in = inbuf; 147 inbytes += ret; 148 } 149 } 150 } 151 /* reset the shift state of the output buffer */ 152 outbytes = OUTBUFSIZE; 153 out = outbuf; 154 ret = iconv(cd, NULL, NULL, &out, &outbytes); 155 if (ret == (size_t)-1) 156 err(EXIT_FAILURE, "iconv()"); 157 if (outbytes < OUTBUFSIZE) 158 (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); 159 160 if (invalids > 0 && !silent) 161 warnx("warning: invalid characters: %lu", 162 (unsigned long)invalids); 163 164 iconv_close(cd); 165} 166 167int 168main(int argc, char **argv) 169{ 170 int ch, i; 171 int opt_l = 0, opt_s = 0, opt_c = 0; 172 char *opt_f = NULL, *opt_t = NULL; 173 FILE *fp; 174 175 setlocale(LC_ALL, ""); 176 setprogname(argv[0]); 177 178 while ((ch = getopt(argc, argv, "cslf:t:")) != EOF) { 179 switch (ch) { 180 case 'c': 181 opt_c = 1; 182 break; 183 case 's': 184 opt_s = 1; 185 break; 186 case 'l': 187 /* list */ 188 opt_l = 1; 189 break; 190 case 'f': 191 /* from */ 192 opt_f = estrdup(optarg); 193 break; 194 case 't': 195 /* to */ 196 opt_t = estrdup(optarg); 197 break; 198 default: 199 usage(); 200 } 201 } 202 argc -= optind; 203 argv += optind; 204 if (opt_l) { 205 if (argc > 0 || opt_s || opt_f != NULL || opt_t != NULL) { 206 warnx("-l is not allowed with other flags."); 207 usage(); 208 } 209 show_codesets(); 210 } else { 211 if (opt_f == NULL) { 212 if (opt_t == NULL) 213 usage(); 214 opt_f = nl_langinfo(CODESET); 215 } else if (opt_t == NULL) 216 opt_t = nl_langinfo(CODESET); 217 218 if (argc == 0) 219 do_conv("<stdin>", stdin, opt_f, opt_t, opt_s, opt_c); 220 else { 221 for (i = 0; i < argc; i++) { 222 fp = fopen(argv[i], "r"); 223 if (fp == NULL) 224 err(EXIT_FAILURE, "Cannot open `%s'", 225 argv[i]); 226 do_conv(argv[i], fp, opt_f, opt_t, opt_s, 227 opt_c); 228 (void)fclose(fp); 229 } 230 } 231 } 232 return EXIT_SUCCESS; 233} 234