iconv.c revision 219019
1219019Sgabor/* $FreeBSD: head/usr.bin/iconv/iconv.c 219019 2011-02-25 00:04:39Z gabor $ */ 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 44219019Sgaborstatic unsigned long long invalids; 45219019Sgabor 46219019Sgaborstatic void do_conv(FILE *, const char *, const char *, bool, bool); 47219019Sgaborstatic int do_list(unsigned int, const char * const *, void *); 48219019Sgaborstatic void usage(void); 49219019Sgabor 50219019Sgaborstruct option long_options[] = 51219019Sgabor{ 52219019Sgabor {"from-code", required_argument, NULL, 'f'}, 53219019Sgabor {"list", no_argument, NULL, 'l'}, 54219019Sgabor {"silent", no_argument, NULL, 's'}, 55219019Sgabor {"to-code", required_argument, NULL, 't'}, 56219019Sgabor {NULL, no_argument, NULL, 0} 57219019Sgabor}; 58219019Sgabor 59219019Sgaborstatic void 60219019Sgaborusage(void) 61219019Sgabor{ 62219019Sgabor (void)fprintf(stderr, 63219019Sgabor "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n" 64219019Sgabor "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n" 65219019Sgabor "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n" 66219019Sgabor "\t%1$s -l\n", getprogname()); 67219019Sgabor exit(1); 68219019Sgabor} 69219019Sgabor 70219019Sgabor#define INBUFSIZE 1024 71219019Sgabor#define OUTBUFSIZE (INBUFSIZE * 2) 72219019Sgaborstatic void 73219019Sgabordo_conv(FILE *fp, const char *from, const char *to, bool silent, 74219019Sgabor bool hide_invalid) 75219019Sgabor{ 76219019Sgabor iconv_t cd; 77219019Sgabor char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *out; 78219019Sgabor char *in; 79219019Sgabor size_t inbytes, outbytes, ret; 80219019Sgabor 81219019Sgabor if ((cd = iconv_open(to, from)) == (iconv_t)-1) 82219019Sgabor err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 83219019Sgabor 84219019Sgabor if (hide_invalid) { 85219019Sgabor int arg = 1; 86219019Sgabor 87219019Sgabor if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) 88219019Sgabor err(1, NULL); 89219019Sgabor } 90219019Sgabor while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) { 91219019Sgabor in = inbuf; 92219019Sgabor while (inbytes > 0) { 93219019Sgabor size_t inval; 94219019Sgabor 95219019Sgabor out = outbuf; 96219019Sgabor outbytes = OUTBUFSIZE; 97219019Sgabor ret = __iconv(cd, &in, &inbytes, &out, &outbytes, 98219019Sgabor 0, &inval); 99219019Sgabor invalids += inval; 100219019Sgabor if (outbytes < OUTBUFSIZE) 101219019Sgabor (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, 102219019Sgabor stdout); 103219019Sgabor if (ret == (size_t)-1 && errno != E2BIG) { 104219019Sgabor if (errno != EINVAL || in == inbuf) 105219019Sgabor err(EXIT_FAILURE, "iconv()"); 106219019Sgabor 107219019Sgabor /* incomplete input character */ 108219019Sgabor (void)memmove(inbuf, in, inbytes); 109219019Sgabor ret = fread(inbuf + inbytes, 1, 110219019Sgabor INBUFSIZE - inbytes, fp); 111219019Sgabor if (ret == 0) { 112219019Sgabor fflush(stdout); 113219019Sgabor if (feof(fp)) 114219019Sgabor errx(EXIT_FAILURE, 115219019Sgabor "unexpected end of file; " 116219019Sgabor "the last character is " 117219019Sgabor "incomplete."); 118219019Sgabor else 119219019Sgabor err(EXIT_FAILURE, "fread()"); 120219019Sgabor } 121219019Sgabor in = inbuf; 122219019Sgabor inbytes += ret; 123219019Sgabor } 124219019Sgabor } 125219019Sgabor } 126219019Sgabor /* reset the shift state of the output buffer */ 127219019Sgabor outbytes = OUTBUFSIZE; 128219019Sgabor out = outbuf; 129219019Sgabor ret = iconv(cd, NULL, NULL, &out, &outbytes); 130219019Sgabor if (ret == (size_t)-1) 131219019Sgabor err(EXIT_FAILURE, "iconv()"); 132219019Sgabor if (outbytes < OUTBUFSIZE) 133219019Sgabor (void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout); 134219019Sgabor 135219019Sgabor if (invalids > 0 && !silent) 136219019Sgabor warnx("warning: invalid characters: %llu", invalids); 137219019Sgabor 138219019Sgabor iconv_close(cd); 139219019Sgabor} 140219019Sgabor 141219019Sgaborstatic int 142219019Sgabordo_list(unsigned int n, const char * const *list, void *data __unused) 143219019Sgabor{ 144219019Sgabor unsigned int i; 145219019Sgabor 146219019Sgabor for(i = 0; i < n; i++) { 147219019Sgabor printf("%s", list[i]); 148219019Sgabor if (i < n - 1) 149219019Sgabor printf(" "); 150219019Sgabor } 151219019Sgabor printf("\n"); 152219019Sgabor 153219019Sgabor return (1); 154219019Sgabor} 155219019Sgabor 156219019Sgaborint 157219019Sgabormain(int argc, char **argv) 158219019Sgabor{ 159219019Sgabor FILE *fp; 160219019Sgabor char *opt_f, *opt_t; 161219019Sgabor int ch, i; 162219019Sgabor bool opt_c = false, opt_s = false; 163219019Sgabor 164219019Sgabor opt_f = opt_t = strdup(""); 165219019Sgabor 166219019Sgabor setlocale(LC_ALL, ""); 167219019Sgabor setprogname(argv[0]); 168219019Sgabor 169219019Sgabor while ((ch = getopt_long(argc, argv, "csLlf:t:", 170219019Sgabor long_options, NULL)) != -1) { 171219019Sgabor switch (ch) { 172219019Sgabor case 'c': 173219019Sgabor opt_c = true; 174219019Sgabor break; 175219019Sgabor case 's': 176219019Sgabor opt_s = true; 177219019Sgabor break; 178219019Sgabor case 'l': 179219019Sgabor /* list */ 180219019Sgabor if (opt_s || opt_c || strcmp(opt_f, "") != 0 || 181219019Sgabor strcmp(opt_t, "") != 0) { 182219019Sgabor warnx("-l is not allowed with other flags."); 183219019Sgabor usage(); 184219019Sgabor } 185219019Sgabor iconvlist(do_list, NULL); 186219019Sgabor return (EXIT_SUCCESS); 187219019Sgabor case 'f': 188219019Sgabor /* from */ 189219019Sgabor if (optarg != NULL) 190219019Sgabor opt_f = strdup(optarg); 191219019Sgabor break; 192219019Sgabor case 't': 193219019Sgabor /* to */ 194219019Sgabor if (optarg != NULL) 195219019Sgabor opt_t = strdup(optarg); 196219019Sgabor break; 197219019Sgabor default: 198219019Sgabor usage(); 199219019Sgabor } 200219019Sgabor } 201219019Sgabor argc -= optind; 202219019Sgabor argv += optind; 203219019Sgabor if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) 204219019Sgabor usage(); 205219019Sgabor if (argc == 0) 206219019Sgabor do_conv(stdin, opt_f, opt_t, opt_s, opt_c); 207219019Sgabor else { 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]); 214219019Sgabor do_conv(fp, opt_f, opt_t, opt_s, 215219019Sgabor opt_c); 216219019Sgabor (void)fclose(fp); 217219019Sgabor } 218219019Sgabor } 219219019Sgabor return (EXIT_SUCCESS); 220219019Sgabor} 221