iconv.c revision 281550
1139749Simp/* $FreeBSD: head/usr.bin/iconv/iconv.c 281550 2015-04-15 09:09:20Z tijl $ */ 226159Sse/* $NetBSD: iconv.c,v 1.16 2009/02/20 15:28:21 yamt Exp $ */ 326159Sse 426159Sse/*- 526159Sse * Copyright (c)2003 Citrus Project, 626159Sse * All rights reserved. 726159Sse * 826159Sse * Redistribution and use in source and binary forms, with or without 926159Sse * modification, are permitted provided that the following conditions 1026159Sse * are met: 1126159Sse * 1. Redistributions of source code must retain the above copyright 1226159Sse * notice, this list of conditions and the following disclaimer. 1326159Sse * 2. Redistributions in binary form must reproduce the above copyright 1426159Sse * notice, this list of conditions and the following disclaimer in the 1526159Sse * documentation and/or other materials provided with the distribution. 1626159Sse * 1726159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1826159Sse * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1926159Sse * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2026159Sse * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2126159Sse * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2226159Sse * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2326159Sse * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2426159Sse * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2526159Sse * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2650477Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2726159Sse * SUCH DAMAGE. 2826159Sse */ 296100Sse 3039231Sgibbs#include <sys/cdefs.h> 31165217Sjhb 3239231Sgibbs#include <err.h> 3339231Sgibbs#include <errno.h> 3439231Sgibbs#include <getopt.h> 3526159Sse#include <iconv.h> 36165217Sjhb#include <limits.h> 37165217Sjhb#include <locale.h> 38165217Sjhb#include <stdbool.h> 396100Sse#include <stdio.h> 40163805Simp#include <stdlib.h> 416100Sse#include <string.h> 42120063Sscottl#include <unistd.h> 43120063Sscottl 44120063Sscottlstatic int do_conv(FILE *, const char *, const char *, bool, bool); 45214122Sjkimstatic int do_list(unsigned int, const char * const *, void *); 46214122Sjkimstatic void usage(void) __dead2; 47214122Sjkim 48120063Sscottlstatic const struct option long_options[] = { 49120063Sscottl {"from-code", required_argument, NULL, 'f'}, 50163163Sjmg {"list", no_argument, NULL, 'l'}, 51163163Sjmg {"silent", no_argument, NULL, 's'}, 52163163Sjmg {"to-code", required_argument, NULL, 't'}, 53163163Sjmg {NULL, no_argument, NULL, 0} 54163163Sjmg}; 55163163Sjmg 56163163Sjmgstatic void 57163163Sjmgusage(void) 58163163Sjmg{ 59163163Sjmg (void)fprintf(stderr, 60163163Sjmg "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n" 61163163Sjmg "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n" 62163163Sjmg "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n" 63163163Sjmg "\t%1$s -l\n", getprogname()); 64167909Sjhb exit(1); 65163163Sjmg} 66163163Sjmg 67163163Sjmg#define INBUFSIZE 1024 68163163Sjmg#define OUTBUFSIZE (INBUFSIZE * 2) 69163163Sjmgstatic int 70163163Sjmgdo_conv(FILE *fp, const char *from, const char *to, bool silent, 71163163Sjmg bool hide_invalid) 72120063Sscottl{ 73120063Sscottl iconv_t cd; 74120063Sscottl char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out; 75164264Sjhb unsigned long long invalids; 76120063Sscottl size_t inbytes, outbytes, ret; 77164264Sjhb 78164264Sjhb if ((cd = iconv_open(to, from)) == (iconv_t)-1) 79164264Sjhb err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 80169221Sjhb 81120063Sscottl if (hide_invalid) { 82120063Sscottl int arg = 1; 83164282Sjhb 84169221Sjhb if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) 85169221Sjhb err(EXIT_FAILURE, NULL); 86169221Sjhb } 87169221Sjhb invalids = 0; 88169221Sjhb while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { 89169221Sjhb in = inbuf; 90169221Sjhb while (inbytes > 0) { 91169221Sjhb size_t inval; 92169221Sjhb 93169221Sjhb out = outbuf; 94169221Sjhb outbytes = OUTBUFSIZE; 95164264Sjhb ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 96164264Sjhb 0, &inval); 97169221Sjhb invalids += inval; 98164282Sjhb if (outbytes < OUTBUFSIZE) 99164264Sjhb (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, 100164264Sjhb stdout); 101164264Sjhb if (ret == (size_t)-1 && errno != E2BIG) { 102164264Sjhb if (errno != EINVAL || in == inbuf) 103169221Sjhb err(EXIT_FAILURE, "iconv()"); 104169221Sjhb 105169221Sjhb /* incomplete input character */ 106169221Sjhb (void)memmove(inbuf, in, inbytes); 107164264Sjhb ret = fread(inbuf + inbytes, 1, 108164264Sjhb INBUFSIZE - inbytes, fp); 109164264Sjhb if (ret == 0) { 110164264Sjhb fflush(stdout); 111180753Sluoqi if (feof(fp)) 112180753Sluoqi errx(EXIT_FAILURE, 113219737Sjhb "unexpected end of file; " 114180753Sluoqi "the last character is " 115180753Sluoqi "incomplete."); 116180753Sluoqi else 117180753Sluoqi err(EXIT_FAILURE, "fread()"); 118180753Sluoqi } 11926159Sse in = inbuf; 12026159Sse inbytes += ret; 12145720Speter } 1226100Sse } 123128019Simp } 124128019Simp /* reset the shift state of the output buffer */ 125128019Simp outbytes = OUTBUFSIZE; 126119266Simp out = outbuf; 127119266Simp ret = iconv(cd, NULL, NULL, &out, &outbytes); 128119266Simp if (ret == (size_t)-1) 129119266Simp err(EXIT_FAILURE, "iconv()"); 1306100Sse if (outbytes < OUTBUFSIZE) 131119266Simp (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); 132119266Simp 1337233Sse if (invalids > 0 && !silent) 134119266Simp warnx("warning: invalid characters: %llu", invalids); 135119266Simp 136119266Simp iconv_close(cd); 137119266Simp return (invalids > 0); 1386100Sse} 139119266Simp 140119266Simpstatic int 141119266Simpdo_list(unsigned int n, const char * const *list, void *data __unused) 142119266Simp{ 1436100Sse unsigned int i; 144119266Simp 145119266Simp for(i = 0; i < n; i++) { 146119266Simp printf("%s", list[i]); 1476100Sse if (i < n - 1) 148119266Simp printf(" "); 149119266Simp } 1507233Sse printf("\n"); 151172394Smarius 152119266Simp return (1); 153119266Simp} 154119266Simp 1557233Sseint 156193256Sjhbmain(int argc, char **argv) 157193256Sjhb{ 158193256Sjhb FILE *fp; 159193256Sjhb char *opt_f, *opt_t; 160180753Sluoqi int ch, i, res; 16126159Sse bool opt_c = false, opt_s = false; 1626100Sse 16326159Sse opt_f = opt_t = strdup(""); 1646100Sse 165165217Sjhb setlocale(LC_ALL, ""); 166165217Sjhb setprogname(argv[0]); 167165217Sjhb 168165217Sjhb while ((ch = getopt_long(argc, argv, "csLlf:t:", 1697233Sse long_options, NULL)) != -1) { 17026159Sse switch (ch) { 17126159Sse case 'c': 17226159Sse opt_c = true; 173119266Simp break; 174119266Simp case 's': 175119266Simp opt_s = true; 176119266Simp break; 177119266Simp case 'l': 178119266Simp /* list */ 179119266Simp if (opt_s || opt_c || strcmp(opt_f, "") != 0 || 18026159Sse strcmp(opt_t, "") != 0) { 1816100Sse warnx("-l is not allowed with other flags."); 18226159Sse usage(); 18326159Sse } 18426159Sse iconvlist(do_list, NULL); 185119266Simp return (EXIT_SUCCESS); 186119266Simp case 'f': 187119266Simp /* from */ 188119266Simp if (optarg != NULL) 189119266Simp opt_f = strdup(optarg); 190119266Simp break; 191119266Simp case 't': 192119266Simp /* to */ 193119266Simp if (optarg != NULL) 194119266Simp opt_t = strdup(optarg); 195119266Simp break; 196119266Simp default: 19726159Sse usage(); 19826159Sse } 199119266Simp } 20039231Sgibbs argc -= optind; 20161047Speter argv += optind; 20261047Speter if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) 20361047Speter usage(); 20461047Speter if (argc == 0) 20561047Speter res = do_conv(stdin, opt_f, opt_t, opt_s, opt_c); 20661047Speter else { 20761047Speter res = 0; 20861047Speter for (i = 0; i < argc; i++) { 20961047Speter fp = (strcmp(argv[i], "-") != 0) ? 21039231Sgibbs fopen(argv[i], "r") : stdin; 21145720Speter if (fp == NULL) 21245720Speter err(EXIT_FAILURE, "Cannot open `%s'", 21345720Speter argv[i]); 21445720Speter res |= do_conv(fp, opt_f, opt_t, opt_s, opt_c); 21545720Speter (void)fclose(fp); 21669953Smsmith } 21769953Smsmith } 21869953Smsmith return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 21969953Smsmith} 22069953Smsmith