1219019Sgabor/* $FreeBSD: stable/11/usr.bin/iconv/iconv.c 330449 2018-03-05 07:26:05Z eadler $ */ 2219019Sgabor/* $NetBSD: iconv.c,v 1.16 2009/02/20 15:28:21 yamt Exp $ */ 3219019Sgabor 4219019Sgabor/*- 5330449Seadler * SPDX-License-Identifier: BSD-2-Clause 6330449Seadler * 7219019Sgabor * Copyright (c)2003 Citrus Project, 8219019Sgabor * All rights reserved. 9219019Sgabor * 10219019Sgabor * Redistribution and use in source and binary forms, with or without 11219019Sgabor * modification, are permitted provided that the following conditions 12219019Sgabor * are met: 13219019Sgabor * 1. Redistributions of source code must retain the above copyright 14219019Sgabor * notice, this list of conditions and the following disclaimer. 15219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 16219019Sgabor * notice, this list of conditions and the following disclaimer in the 17219019Sgabor * documentation and/or other materials provided with the distribution. 18219019Sgabor * 19219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22219019Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29219019Sgabor * SUCH DAMAGE. 30219019Sgabor */ 31219019Sgabor 32219019Sgabor#include <sys/cdefs.h> 33219019Sgabor 34219019Sgabor#include <err.h> 35219019Sgabor#include <errno.h> 36219019Sgabor#include <getopt.h> 37219019Sgabor#include <iconv.h> 38219019Sgabor#include <limits.h> 39219019Sgabor#include <locale.h> 40219019Sgabor#include <stdbool.h> 41219019Sgabor#include <stdio.h> 42219019Sgabor#include <stdlib.h> 43219019Sgabor#include <string.h> 44219019Sgabor#include <unistd.h> 45219019Sgabor 46267439Stijlstatic int do_conv(FILE *, const char *, const char *, bool, bool); 47267439Stijlstatic int do_list(unsigned int, const char * const *, void *); 48267439Stijlstatic void usage(void) __dead2; 49219019Sgabor 50267439Stijlstatic const struct option long_options[] = { 51219019Sgabor {"from-code", required_argument, NULL, 'f'}, 52219019Sgabor {"list", no_argument, NULL, 'l'}, 53219019Sgabor {"silent", no_argument, NULL, 's'}, 54219019Sgabor {"to-code", required_argument, NULL, 't'}, 55219019Sgabor {NULL, no_argument, NULL, 0} 56219019Sgabor}; 57219019Sgabor 58219019Sgaborstatic void 59219019Sgaborusage(void) 60219019Sgabor{ 61219019Sgabor (void)fprintf(stderr, 62219019Sgabor "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n" 63219019Sgabor "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n" 64219019Sgabor "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n" 65219019Sgabor "\t%1$s -l\n", getprogname()); 66219019Sgabor exit(1); 67219019Sgabor} 68219019Sgabor 69219019Sgabor#define INBUFSIZE 1024 70219019Sgabor#define OUTBUFSIZE (INBUFSIZE * 2) 71267439Stijlstatic int 72219019Sgabordo_conv(FILE *fp, const char *from, const char *to, bool silent, 73219019Sgabor bool hide_invalid) 74219019Sgabor{ 75219019Sgabor iconv_t cd; 76281550Stijl char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out; 77267439Stijl unsigned long long invalids; 78219019Sgabor size_t inbytes, outbytes, ret; 79219019Sgabor 80219019Sgabor if ((cd = iconv_open(to, from)) == (iconv_t)-1) 81219019Sgabor err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from); 82219019Sgabor 83219019Sgabor if (hide_invalid) { 84219019Sgabor int arg = 1; 85219019Sgabor 86219019Sgabor if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1) 87267439Stijl err(EXIT_FAILURE, NULL); 88219019Sgabor } 89267439Stijl invalids = 0; 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); 139267439Stijl return (invalids > 0); 140219019Sgabor} 141219019Sgabor 142219019Sgaborstatic int 143219019Sgabordo_list(unsigned int n, const char * const *list, void *data __unused) 144219019Sgabor{ 145219019Sgabor unsigned int i; 146219019Sgabor 147219019Sgabor for(i = 0; i < n; i++) { 148219019Sgabor printf("%s", list[i]); 149219019Sgabor if (i < n - 1) 150219019Sgabor printf(" "); 151219019Sgabor } 152219019Sgabor printf("\n"); 153219019Sgabor 154219019Sgabor return (1); 155219019Sgabor} 156219019Sgabor 157219019Sgaborint 158219019Sgabormain(int argc, char **argv) 159219019Sgabor{ 160219019Sgabor FILE *fp; 161287319Sdelphij const char *opt_f, *opt_t; 162267439Stijl int ch, i, res; 163219019Sgabor bool opt_c = false, opt_s = false; 164219019Sgabor 165287319Sdelphij opt_f = opt_t = ""; 166219019Sgabor 167219019Sgabor setlocale(LC_ALL, ""); 168219019Sgabor setprogname(argv[0]); 169219019Sgabor 170219019Sgabor while ((ch = getopt_long(argc, argv, "csLlf:t:", 171219019Sgabor long_options, NULL)) != -1) { 172219019Sgabor switch (ch) { 173219019Sgabor case 'c': 174219019Sgabor opt_c = true; 175219019Sgabor break; 176219019Sgabor case 's': 177219019Sgabor opt_s = true; 178219019Sgabor break; 179219019Sgabor case 'l': 180219019Sgabor /* list */ 181219019Sgabor if (opt_s || opt_c || strcmp(opt_f, "") != 0 || 182219019Sgabor strcmp(opt_t, "") != 0) { 183219019Sgabor warnx("-l is not allowed with other flags."); 184219019Sgabor usage(); 185219019Sgabor } 186219019Sgabor iconvlist(do_list, NULL); 187219019Sgabor return (EXIT_SUCCESS); 188219019Sgabor case 'f': 189219019Sgabor /* from */ 190219019Sgabor if (optarg != NULL) 191287319Sdelphij opt_f = optarg; 192219019Sgabor break; 193219019Sgabor case 't': 194219019Sgabor /* to */ 195219019Sgabor if (optarg != NULL) 196287319Sdelphij opt_t = optarg; 197219019Sgabor break; 198219019Sgabor default: 199219019Sgabor usage(); 200219019Sgabor } 201219019Sgabor } 202219019Sgabor argc -= optind; 203219019Sgabor argv += optind; 204219019Sgabor if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0)) 205219019Sgabor usage(); 206219019Sgabor if (argc == 0) 207267439Stijl res = do_conv(stdin, opt_f, opt_t, opt_s, opt_c); 208219019Sgabor else { 209267439Stijl res = 0; 210219019Sgabor for (i = 0; i < argc; i++) { 211219019Sgabor fp = (strcmp(argv[i], "-") != 0) ? 212219019Sgabor fopen(argv[i], "r") : stdin; 213219019Sgabor if (fp == NULL) 214219019Sgabor err(EXIT_FAILURE, "Cannot open `%s'", 215219019Sgabor argv[i]); 216267439Stijl res |= do_conv(fp, opt_f, opt_t, opt_s, opt_c); 217219019Sgabor (void)fclose(fp); 218219019Sgabor } 219219019Sgabor } 220267439Stijl return (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 221219019Sgabor} 222