1 /* $NetBSD: iconv.c,v 1.4 2003/10/20 12:56:18 yamt 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.4 2003/10/20 12:56:18 yamt Exp $"); 32 #endif /* LIBC_SCCS and not lint */ 33 34 #include <errno.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <iconv.h> 39 #include <unistd.h> 40 #include <err.h> 41 42 static void 43 usage(void) 44 { 45 fprintf(stderr, 46 "usage:\n" 47 "\t%s [-cs] -f <from> -t <to> [file ...]\n" 48 "\t%s -l\n", 49 getprogname(), getprogname()); 50 exit(1); 51 } 52 53 static void 54 show_codesets(void) 55 { 56 char **list; 57 size_t sz, i; 58 59 if (__iconv_get_list(&list, &sz)) 60 err(EXIT_FAILURE, "__iconv_get_list()"); 61 62 for (i=0; i<sz; i++) { 63 printf("%s\n", list[i]); 64 } 65 66 __iconv_free_list(list, sz); 67 } 68 69 #define INBUFSIZE 1024 70 #define OUTBUFSIZE (INBUFSIZE*2) 71 static void 72 do_conv(const char *fn, FILE *fp, const char *from, const char *to, int silent, 73 int hide_invalid) 74 { 75 char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *out; 76 const char *in; 77 size_t inbytes, outbytes, ret, invalids; 78 iconv_t cd; 79 u_int32_t flags = 0; 80 81 if (hide_invalid) 82 flags |= __ICONV_F_HIDE_INVALID; 83 cd = iconv_open(to, from); 84 if (cd == (iconv_t)-1) 85 err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 86 87 invalids = 0; 88 while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { 89 in = inbuf; 90 while (inbytes>0) { 91 size_t inval; 92 93 out = outbuf; 94 outbytes = OUTBUFSIZE; 95 ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 96 flags, &inval); 97 invalids += inval; 98 if (ret==(size_t)-1 && errno != E2BIG) { 99 /* 100 * XXX: iconv(3) is bad interface. 101 * invalid character count is lost here. 102 * instead, we just provide __iconv function. 103 */ 104 if (errno != EINVAL || in == inbuf) 105 err(EXIT_FAILURE, "iconv()"); 106 107 /* incomplete input character */ 108 memmove(inbuf, in, inbytes); 109 ret = fread(inbuf+inbytes, 1, 110 INBUFSIZE-inbytes, fp); 111 if (ret == 0) { 112 if (feof(fp)) 113 errx(EXIT_FAILURE, 114 "iconv(): %s", 115 strerror(EINVAL)); 116 else 117 err(EXIT_FAILURE, "fread()"); 118 } 119 in = inbuf; 120 inbytes += ret; 121 } 122 if (outbytes < OUTBUFSIZE) 123 fwrite(outbuf, 1, OUTBUFSIZE-outbytes, stdout); 124 } 125 } 126 /* reset the shift state of the output buffer */ 127 outbytes = OUTBUFSIZE; 128 out = outbuf; 129 ret = iconv(cd, NULL, NULL, &out, &outbytes); 130 if (ret == -1) 131 err(EXIT_FAILURE, "iconv()"); 132 if (outbytes < OUTBUFSIZE) 133 fwrite(outbuf, 1, OUTBUFSIZE-outbytes, stdout); 134 135 if (invalids > 0 && !silent) 136 warnx("warning: invalid characters: %lu", 137 (unsigned long)invalids); 138 139 iconv_close(cd); 140 } 141 142 int 143 main(int argc, char **argv) 144 { 145 int ch, i; 146 extern char *optarg; 147 extern int optind; 148 int opt_l = 0, opt_s = 0, opt_c = 0; 149 char *opt_f = NULL, *opt_t = NULL; 150 FILE *fp; 151 152 while ((ch=getopt(argc, argv, "cslf:t:")) != EOF) { 153 switch (ch) { 154 case 'c': 155 opt_c = 1; 156 break; 157 case 's': 158 opt_s = 1; 159 break; 160 case 'l': 161 /* list */ 162 opt_l = 1; 163 break; 164 case 'f': 165 /* from */ 166 opt_f = strdup(optarg); 167 break; 168 case 't': 169 /* to */ 170 opt_t = strdup(optarg); 171 break; 172 default: 173 usage(); 174 } 175 } 176 argc-=optind; 177 argv+=optind; 178 if (opt_l) { 179 if (argc>0 || opt_s || opt_f != NULL || opt_t != NULL) { 180 warnx("%s: -l should be specified solely.", 181 getprogname()); 182 usage(); 183 } 184 show_codesets(); 185 } else { 186 if (opt_f == NULL || opt_t == NULL) 187 usage(); 188 189 if (argc == 0) 190 do_conv("<stdin>", stdin, opt_f, opt_t, opt_s, opt_c); 191 else { 192 for (i=0; i<argc; i++) { 193 fp = fopen(argv[i], "r"); 194 if (fp == NULL) 195 errx(EXIT_FAILURE, "%s: %s:%s", 196 getprogname(), argv[i], 197 strerror(errno)); 198 do_conv(argv[i], fp, opt_f, opt_t, opt_s, 199 opt_c); 200 fclose(fp); 201 } 202 } 203 } 204 205 return EXIT_SUCCESS; 206 } 207