iconv.c revision 287319
1219019Sgabor/* $FreeBSD: head/usr.bin/iconv/iconv.c 287319 2015-08-31 05:57:26Z delphij $ */ 2219019Sgabor/* $NetBSD: iconv.c,v 1.16 2009/02/20 15:28:21 yamt Exp $ */ 3219019Sgabor 4219019Sgabor/*- 5219019Sgabor * Copyright (c)2003 Citrus Project, 6219019Sgabor * All rights reserved. 7219019Sgabor * 8219019Sgabor * Redistribution and use in source and binary forms, with or without 9219019Sgabor * modification, are permitted provided that the following conditions 10219019Sgabor * are met: 11219019Sgabor * 1. Redistributions of source code must retain the above copyright 12219019Sgabor * notice, this list of conditions and the following disclaimer. 13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 14219019Sgabor * notice, this list of conditions and the following disclaimer in the 15219019Sgabor * documentation and/or other materials provided with the distribution. 16219019Sgabor * 17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20219019Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219019Sgabor * SUCH DAMAGE. 28219019Sgabor */ 29219019Sgabor 30219019Sgabor#include <sys/cdefs.h> 31219019Sgabor 32219019Sgabor#include <err.h> 33219019Sgabor#include <errno.h> 34219019Sgabor#include <getopt.h> 35219019Sgabor#include <iconv.h> 36219019Sgabor#include <limits.h> 37219019Sgabor#include <locale.h> 38219019Sgabor#include <stdbool.h> 39219019Sgabor#include <stdio.h> 40219019Sgabor#include <stdlib.h> 41219019Sgabor#include <string.h> 42219019Sgabor#include <unistd.h> 43219019Sgabor 44267439Stijlstatic int do_conv(FILE *, const char *, const char *, bool, bool); 45267439Stijlstatic int do_list(unsigned int, const char * const *, void *); 46267439Stijlstatic void usage(void) __dead2; 47219019Sgabor 48267439Stijlstatic const struct option long_options[] = { 49219019Sgabor {"from-code", required_argument, NULL, 'f'}, 50219019Sgabor {"list", no_argument, NULL, 'l'}, 51219019Sgabor {"silent", no_argument, NULL, 's'}, 52219019Sgabor {"to-code", required_argument, NULL, 't'}, 53219019Sgabor {NULL, no_argument, NULL, 0} 54219019Sgabor}; 55219019Sgabor 56219019Sgaborstatic void 57219019Sgaborusage(void) 58219019Sgabor{ 59219019Sgabor (void)fprintf(stderr, 60219019Sgabor "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n" 61219019Sgabor "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n" 62219019Sgabor "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n" 63219019Sgabor "\t%1$s -l\n", getprogname()); 64219019Sgabor exit(1); 65219019Sgabor} 66219019Sgabor 67219019Sgabor#define INBUFSIZE 1024 68219019Sgabor#define OUTBUFSIZE (INBUFSIZE * 2) 69267439Stijlstatic int 70219019Sgabordo_conv(FILE *fp, const char *from, const char *to, bool silent, 71219019Sgabor bool hide_invalid) 72219019Sgabor{ 73219019Sgabor iconv_t cd; 74281550Stijl char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out; 75267439Stijl unsigned long long invalids; 76219019Sgabor size_t inbytes, outbytes, ret; 77219019Sgabor 78219019Sgabor if ((cd = iconv_open(to, from)) == (iconv_t)-1) 79219019Sgabor err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 80219019Sgabor 81219019Sgabor if (hide_invalid) { 82219019Sgabor int arg = 1; 83219019Sgabor 84219019Sgabor if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) 85267439Stijl err(EXIT_FAILURE, NULL); 86219019Sgabor } 87267439Stijl invalids = 0; 88219019Sgabor while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { 89219019Sgabor in = inbuf; 90219019Sgabor while (inbytes > 0) { 91219019Sgabor size_t inval; 92219019Sgabor 93219019Sgabor out = outbuf; 94219019Sgabor outbytes = OUTBUFSIZE; 95219019Sgabor ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 96219019Sgabor 0, &inval); 97219019Sgabor invalids += inval; 98219019Sgabor if (outbytes < OUTBUFSIZE) 99219019Sgabor (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, 100219019Sgabor stdout); 101219019Sgabor if (ret == (size_t)-1 && errno != E2BIG) { 102219019Sgabor if (errno != EINVAL || in == inbuf) 103219019Sgabor err(EXIT_FAILURE, "iconv()"); 104219019Sgabor 105219019Sgabor /* incomplete input character */ 106219019Sgabor (void)memmove(inbuf, in, inbytes); 107219019Sgabor ret = fread(inbuf + inbytes, 1, 108219019Sgabor INBUFSIZE - inbytes, fp); 109219019Sgabor if (ret == 0) { 110219019Sgabor fflush(stdout); 111219019Sgabor if (feof(fp)) 112219019Sgabor errx(EXIT_FAILURE, 113219019Sgabor "unexpected end of file; " 114219019Sgabor "the last character is " 115219019Sgabor "incomplete."); 116219019Sgabor else 117219019Sgabor err(EXIT_FAILURE, "fread()"); 118219019Sgabor } 119219019Sgabor in = inbuf; 120219019Sgabor inbytes += ret; 121219019Sgabor } 122219019Sgabor } 123219019Sgabor } 124219019Sgabor /* reset the shift state of the output buffer */ 125219019Sgabor outbytes = OUTBUFSIZE; 126219019Sgabor out = outbuf; 127219019Sgabor ret = iconv(cd, NULL, NULL, &out, &outbytes); 128219019Sgabor if (ret == (size_t)-1) 129219019Sgabor err(EXIT_FAILURE, "iconv()"); 130219019Sgabor if (outbytes < OUTBUFSIZE) 131219019Sgabor (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); 132219019Sgabor 133219019Sgabor if (invalids > 0 && !silent) 134219019Sgabor warnx("warning: invalid characters: %llu", invalids); 135219019Sgabor 136219019Sgabor iconv_close(cd); 137267439Stijl return (invalids > 0); 138219019Sgabor} 139219019Sgabor 140219019Sgaborstatic int 141219019Sgabordo_list(unsigned int n, const char * const *list, void *data __unused) 142219019Sgabor{ 143219019Sgabor unsigned int i; 144219019Sgabor 145219019Sgabor for(i = 0; i < n; i++) { 146219019Sgabor printf("%s", list[i]); 147219019Sgabor if (i < n - 1) 148219019Sgabor printf(" "); 149219019Sgabor } 150219019Sgabor printf("\n"); 151219019Sgabor 152219019Sgabor return (1); 153219019Sgabor} 154219019Sgabor 155219019Sgaborint 156219019Sgabormain(int argc, char **argv) 157219019Sgabor{ 158219019Sgabor FILE *fp; 159287319Sdelphij const char *opt_f, *opt_t; 160267439Stijl int ch, i, res; 161219019Sgabor bool opt_c = false, opt_s = false; 162219019Sgabor 163287319Sdelphij opt_f = opt_t = ""; 164219019Sgabor 165219019Sgabor setlocale(LC_ALL, ""); 166219019Sgabor setprogname(argv[0]); 167219019Sgabor 168219019Sgabor while ((ch = getopt_long(argc, argv, "csLlf:t:", 169219019Sgabor long_options, NULL)) != -1) { 170219019Sgabor switch (ch) { 171219019Sgabor case 'c': 172219019Sgabor opt_c = true; 173219019Sgabor break; 174219019Sgabor case 's': 175219019Sgabor opt_s = true; 176219019Sgabor break; 177219019Sgabor case 'l': 178219019Sgabor /* list */ 179219019Sgabor if (opt_s || opt_c || strcmp(opt_f, "") != 0 || 180219019Sgabor strcmp(opt_t, "") != 0) { 181219019Sgabor warnx("-l is not allowed with other flags."); 182219019Sgabor usage(); 183219019Sgabor } 184219019Sgabor iconvlist(do_list, NULL); 185219019Sgabor return (EXIT_SUCCESS); 186219019Sgabor case 'f': 187219019Sgabor /* from */ 188219019Sgabor if (optarg != NULL) 189287319Sdelphij opt_f = optarg; 190219019Sgabor break; 191219019Sgabor case 't': 192219019Sgabor /* to */ 193219019Sgabor if (optarg != NULL) 194287319Sdelphij opt_t = optarg; 195219019Sgabor break; 196219019Sgabor default: 197219019Sgabor usage(); 198219019Sgabor } 199219019Sgabor } 200219019Sgabor argc -= optind; 201219019Sgabor argv += optind; 202219019Sgabor if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) 203219019Sgabor usage(); 204219019Sgabor if (argc == 0) 205267439Stijl res = do_conv(stdin, opt_f, opt_t, opt_s, opt_c); 206219019Sgabor else { 207267439Stijl res = 0; 208219019Sgabor for (i = 0; i < argc; i++) { 209219019Sgabor fp = (strcmp(argv[i], "-") != 0) ? 210219019Sgabor fopen(argv[i], "r") : stdin; 211219019Sgabor if (fp == NULL) 212219019Sgabor err(EXIT_FAILURE, "Cannot open `%s'", 213219019Sgabor argv[i]); 214267439Stijl res |= do_conv(fp, opt_f, opt_t, opt_s, opt_c); 215219019Sgabor (void)fclose(fp); 216219019Sgabor } 217219019Sgabor } 218267439Stijl return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 219219019Sgabor} 220