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