iconv.c revision 287794
1193323Sed/* $FreeBSD: stable/10/usr.bin/iconv/iconv.c 287794 2015-09-14 18:52:41Z delphij $ */ 2193323Sed/* $NetBSD: iconv.c,v 1.16 2009/02/20 15:28:21 yamt Exp $ */ 3193323Sed 4193323Sed/*- 5193323Sed * Copyright (c)2003 Citrus Project, 6193323Sed * All rights reserved. 7193323Sed * 8193323Sed * Redistribution and use in source and binary forms, with or without 9193323Sed * modification, are permitted provided that the following conditions 10193323Sed * are met: 11193323Sed * 1. Redistributions of source code must retain the above copyright 12193323Sed * notice, this list of conditions and the following disclaimer. 13193323Sed * 2. Redistributions in binary form must reproduce the above copyright 14193323Sed * notice, this list of conditions and the following disclaimer in the 15193323Sed * documentation and/or other materials provided with the distribution. 16193323Sed * 17193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23198090Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24198090Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27193323Sed * SUCH DAMAGE. 28198090Srdivacky */ 29193323Sed 30193323Sed#include <sys/cdefs.h> 31193323Sed 32249423Sdim#include <err.h> 33193323Sed#include <errno.h> 34193323Sed#include <getopt.h> 35193323Sed#include <iconv.h> 36193323Sed#include <limits.h> 37193323Sed#include <locale.h> 38249423Sdim#include <stdbool.h> 39193323Sed#include <stdio.h> 40193323Sed#include <stdlib.h> 41193323Sed#include <string.h> 42193323Sed#include <unistd.h> 43193323Sed 44193323Sedstatic int do_conv(FILE *, const char *, const char *, bool, bool); 45193323Sedstatic int do_list(unsigned int, const char * const *, void *); 46193323Sedstatic void usage(void) __dead2; 47193323Sed 48202375Srdivackystatic const struct option long_options[] = { 49202375Srdivacky {"from-code", required_argument, NULL, 'f'}, 50193323Sed {"list", no_argument, NULL, 'l'}, 51193323Sed {"silent", no_argument, NULL, 's'}, 52193323Sed {"to-code", required_argument, NULL, 't'}, 53193323Sed {NULL, no_argument, NULL, 0} 54193323Sed}; 55202375Srdivacky 56202375Srdivackystatic void 57193323Sedusage(void) 58198090Srdivacky{ 59193323Sed (void)fprintf(stderr, 60226633Sdim "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n" 61218893Sdim "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n" 62193323Sed "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n" 63193323Sed "\t%1$s -l\n", getprogname()); 64193323Sed exit(1); 65193323Sed} 66193323Sed 67193323Sed#define INBUFSIZE 1024 68193323Sed#define OUTBUFSIZE (INBUFSIZE * 2) 69193323Sedstatic int 70193323Seddo_conv(FILE *fp, const char *from, const char *to, bool silent, 71193323Sed bool hide_invalid) 72193323Sed{ 73193323Sed iconv_t cd; 74193323Sed char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out; 75193323Sed unsigned long long invalids; 76193323Sed size_t inbytes, outbytes, ret; 77193323Sed 78193323Sed if ((cd = iconv_open(to, from)) == (iconv_t)-1) 79193323Sed err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 80224145Sdim 81193323Sed if (hide_invalid) { 82193323Sed int arg = 1; 83193323Sed 84193323Sed if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) 85193323Sed err(EXIT_FAILURE, NULL); 86205218Srdivacky } 87193323Sed invalids = 0; 88193323Sed while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { 89193323Sed in = inbuf; 90193323Sed while (inbytes > 0) { 91263508Sdim size_t inval; 92193323Sed 93193323Sed out = outbuf; 94193323Sed outbytes = OUTBUFSIZE; 95193323Sed ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 96193323Sed 0, &inval); 97193323Sed invalids += inval; 98193323Sed if (outbytes < OUTBUFSIZE) 99193323Sed (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, 100193323Sed stdout); 101193323Sed if (ret == (size_t)-1 && errno != E2BIG) { 102193323Sed if (errno != EINVAL || in == inbuf) 103193323Sed err(EXIT_FAILURE, "iconv()"); 104193323Sed 105193323Sed /* incomplete input character */ 106193323Sed (void)memmove(inbuf, in, inbytes); 107193323Sed ret = fread(inbuf + inbytes, 1, 108193323Sed INBUFSIZE - inbytes, fp); 109193323Sed if (ret == 0) { 110218893Sdim fflush(stdout); 111193323Sed if (feof(fp)) 112193323Sed errx(EXIT_FAILURE, 113193323Sed "unexpected end of file; " 114226633Sdim "the last character is " 115226633Sdim "incomplete."); 116226633Sdim else 117226633Sdim err(EXIT_FAILURE, "fread()"); 118226633Sdim } 119226633Sdim in = inbuf; 120193323Sed inbytes += ret; 121193323Sed } 122263508Sdim } 123207618Srdivacky } 124207618Srdivacky /* reset the shift state of the output buffer */ 125193323Sed outbytes = OUTBUFSIZE; 126193323Sed out = outbuf; 127193323Sed ret = iconv(cd, NULL, NULL, &out, &outbytes); 128193323Sed if (ret == (size_t)-1) 129193323Sed err(EXIT_FAILURE, "iconv()"); 130193323Sed if (outbytes < OUTBUFSIZE) 131207618Srdivacky (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); 132207618Srdivacky 133193323Sed if (invalids > 0 && !silent) 134193323Sed warnx("warning: invalid characters: %llu", invalids); 135193323Sed 136193323Sed iconv_close(cd); 137263508Sdim return (invalids > 0); 138193323Sed} 139193323Sed 140193323Sedstatic int 141193323Seddo_list(unsigned int n, const char * const *list, void *data __unused) 142193323Sed{ 143198090Srdivacky unsigned int i; 144193323Sed 145193323Sed for(i = 0; i < n; i++) { 146193323Sed printf("%s", list[i]); 147218893Sdim if (i < n - 1) 148218893Sdim printf(" "); 149218893Sdim } 150193323Sed printf("\n"); 151263508Sdim 152193323Sed return (1); 153193323Sed} 154193323Sed 155198090Srdivackyint 156193323Sedmain(int argc, char **argv) 157193323Sed{ 158249423Sdim FILE *fp; 159249423Sdim const char *opt_f, *opt_t; 160249423Sdim int ch, i, res; 161249423Sdim bool opt_c = false, opt_s = false; 162249423Sdim 163249423Sdim opt_f = opt_t = ""; 164263508Sdim 165193323Sed setlocale(LC_ALL, ""); 166193323Sed setprogname(argv[0]); 167193323Sed 168198090Srdivacky while ((ch = getopt_long(argc, argv, "csLlf:t:", 169193323Sed long_options, NULL)) != -1) { 170249423Sdim switch (ch) { 171249423Sdim case 'c': 172249423Sdim opt_c = true; 173249423Sdim break; 174249423Sdim case 's': 175249423Sdim opt_s = true; 176263508Sdim break; 177193323Sed case 'l': 178193323Sed /* list */ 179193323Sed if (opt_s || opt_c || strcmp(opt_f, "") != 0 || 180193323Sed strcmp(opt_t, "") != 0) { 181193323Sed warnx("-l is not allowed with other flags."); 182263508Sdim usage(); 183193323Sed } 184198090Srdivacky iconvlist(do_list, NULL); 185198090Srdivacky return (EXIT_SUCCESS); 186193323Sed case 'f': 187193323Sed /* from */ 188193323Sed if (optarg != NULL) 189193323Sed opt_f = optarg; 190193323Sed break; 191193323Sed case 't': 192193323Sed /* to */ 193219077Sdim if (optarg != NULL) 194193323Sed opt_t = optarg; 195193323Sed break; 196193323Sed default: 197193323Sed usage(); 198193323Sed } 199193323Sed } 200219077Sdim argc -= optind; 201219077Sdim argv += optind; 202193323Sed if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) 203193323Sed usage(); 204193323Sed if (argc == 0) 205193323Sed res = do_conv(stdin, opt_f, opt_t, opt_s, opt_c); 206219077Sdim else { 207219077Sdim res = 0; 208193323Sed for (i = 0; i < argc; i++) { 209193323Sed fp = (strcmp(argv[i], "-") != 0) ? 210193323Sed fopen(argv[i], "r") : stdin; 211193323Sed if (fp == NULL) 212193323Sed err(EXIT_FAILURE, "Cannot open `%s'", 213219077Sdim argv[i]); 214193323Sed res |= do_conv(fp, opt_f, opt_t, opt_s, opt_c); 215193323Sed (void)fclose(fp); 216193323Sed } 217193323Sed } 218193323Sed return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 219193323Sed} 220193323Sed