xref: /dragonfly/usr.bin/iconv/iconv.c (revision 0db70a6a)
10d5acd74SJohn Marino /* $FreeBSD: head/usr.bin/iconv/iconv.c 252583 2013-07-03 18:27:45Z peter $ */
20d5acd74SJohn Marino /* $NetBSD: iconv.c,v 1.16 2009/02/20 15:28:21 yamt Exp $ */
3af0d08c8SJoerg Sonnenberger 
4af0d08c8SJoerg Sonnenberger /*-
5af0d08c8SJoerg Sonnenberger  * Copyright (c)2003 Citrus Project,
6af0d08c8SJoerg Sonnenberger  * All rights reserved.
7af0d08c8SJoerg Sonnenberger  *
8af0d08c8SJoerg Sonnenberger  * Redistribution and use in source and binary forms, with or without
9af0d08c8SJoerg Sonnenberger  * modification, are permitted provided that the following conditions
10af0d08c8SJoerg Sonnenberger  * are met:
11af0d08c8SJoerg Sonnenberger  * 1. Redistributions of source code must retain the above copyright
12af0d08c8SJoerg Sonnenberger  *    notice, this list of conditions and the following disclaimer.
13af0d08c8SJoerg Sonnenberger  * 2. Redistributions in binary form must reproduce the above copyright
14af0d08c8SJoerg Sonnenberger  *    notice, this list of conditions and the following disclaimer in the
15af0d08c8SJoerg Sonnenberger  *    documentation and/or other materials provided with the distribution.
16af0d08c8SJoerg Sonnenberger  *
17af0d08c8SJoerg Sonnenberger  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18af0d08c8SJoerg Sonnenberger  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19af0d08c8SJoerg Sonnenberger  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20af0d08c8SJoerg Sonnenberger  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21af0d08c8SJoerg Sonnenberger  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22af0d08c8SJoerg Sonnenberger  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23af0d08c8SJoerg Sonnenberger  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24af0d08c8SJoerg Sonnenberger  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25af0d08c8SJoerg Sonnenberger  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26af0d08c8SJoerg Sonnenberger  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27af0d08c8SJoerg Sonnenberger  * SUCH DAMAGE.
28af0d08c8SJoerg Sonnenberger  */
29af0d08c8SJoerg Sonnenberger 
300d5acd74SJohn Marino #include <sys/cdefs.h>
310d5acd74SJohn Marino 
32af0d08c8SJoerg Sonnenberger #include <err.h>
33af0d08c8SJoerg Sonnenberger #include <errno.h>
340d5acd74SJohn Marino #include <getopt.h>
35af0d08c8SJoerg Sonnenberger #include <iconv.h>
360d5acd74SJohn Marino #include <limits.h>
370d5acd74SJohn Marino #include <locale.h>
380d5acd74SJohn Marino #include <stdbool.h>
39af0d08c8SJoerg Sonnenberger #include <stdio.h>
40af0d08c8SJoerg Sonnenberger #include <stdlib.h>
41af0d08c8SJoerg Sonnenberger #include <string.h>
42af0d08c8SJoerg Sonnenberger #include <unistd.h>
43af0d08c8SJoerg Sonnenberger 
440d5acd74SJohn Marino static unsigned long long invalids;
450d5acd74SJohn Marino 
460d5acd74SJohn Marino static void		 do_conv(FILE *, const char *, const char *, bool, bool);
470d5acd74SJohn Marino static int		 do_list(unsigned int, const char * const *, void *);
480d5acd74SJohn Marino static void		 usage(void);
490d5acd74SJohn Marino 
500d5acd74SJohn Marino static struct option long_options[] = {
510d5acd74SJohn Marino 	{"from-code",		required_argument,	NULL, 'f'},
520d5acd74SJohn Marino 	{"list",		no_argument,		NULL, 'l'},
530d5acd74SJohn Marino 	{"silent",		no_argument,		NULL, 's'},
540d5acd74SJohn Marino         {"to-code",		required_argument,	NULL, 't'},
550d5acd74SJohn Marino         {NULL,                  no_argument,            NULL, 0}
560d5acd74SJohn Marino };
570d5acd74SJohn Marino 
58af0d08c8SJoerg Sonnenberger static void
usage(void)59af0d08c8SJoerg Sonnenberger usage(void)
60af0d08c8SJoerg Sonnenberger {
610d5acd74SJohn Marino 	(void)fprintf(stderr,
620d5acd74SJohn Marino 	    "Usage:\t%1$s [-cs] -f <from_code> -t <to_code> [file ...]\n"
630d5acd74SJohn Marino 	    "\t%1$s -f <from_code> [-cs] [-t <to_code>] [file ...]\n"
640d5acd74SJohn Marino 	    "\t%1$s -t <to_code> [-cs] [-f <from_code>] [file ...]\n"
650d5acd74SJohn Marino 	    "\t%1$s -l\n", getprogname());
66af0d08c8SJoerg Sonnenberger 	exit(1);
67af0d08c8SJoerg Sonnenberger }
68af0d08c8SJoerg Sonnenberger 
69af0d08c8SJoerg Sonnenberger #define INBUFSIZE 1024
70af0d08c8SJoerg Sonnenberger #define OUTBUFSIZE (INBUFSIZE * 2)
71af0d08c8SJoerg Sonnenberger static void
do_conv(FILE * fp,const char * from,const char * to,bool silent,bool hide_invalid)720d5acd74SJohn Marino do_conv(FILE *fp, const char *from, const char *to, bool silent,
730d5acd74SJohn Marino     bool hide_invalid)
74af0d08c8SJoerg Sonnenberger {
750d5acd74SJohn Marino 	iconv_t cd;
76*0db70a6aSJohn Marino 	char inbuf[INBUFSIZE], outbuf[OUTBUFSIZE], *in, *out;
770d5acd74SJohn Marino 	size_t inbytes, outbytes, ret;
78af0d08c8SJoerg Sonnenberger 
790d5acd74SJohn Marino 	if ((cd = iconv_open(to, from)) == (iconv_t)-1)
80af0d08c8SJoerg Sonnenberger 		err(EXIT_FAILURE, "iconv_open(%s, %s)", to, from);
81af0d08c8SJoerg Sonnenberger 
820d5acd74SJohn Marino 	if (hide_invalid) {
830d5acd74SJohn Marino 		int arg = 1;
840d5acd74SJohn Marino 
850d5acd74SJohn Marino 		if (iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, (void *)&arg) == -1)
860d5acd74SJohn Marino 			err(1, NULL);
870d5acd74SJohn Marino 	}
88af0d08c8SJoerg Sonnenberger 	while ((inbytes = fread(inbuf, 1, INBUFSIZE, fp)) > 0) {
89af0d08c8SJoerg Sonnenberger 		in = inbuf;
90af0d08c8SJoerg Sonnenberger 		while (inbytes > 0) {
91af0d08c8SJoerg Sonnenberger 			size_t inval;
92af0d08c8SJoerg Sonnenberger 
93af0d08c8SJoerg Sonnenberger 			out = outbuf;
94af0d08c8SJoerg Sonnenberger 			outbytes = OUTBUFSIZE;
95af0d08c8SJoerg Sonnenberger 			ret = __iconv(cd, &in, &inbytes, &out, &outbytes,
960d5acd74SJohn Marino 			    0, &inval);
97af0d08c8SJoerg Sonnenberger 			invalids += inval;
980d5acd74SJohn Marino 			if (outbytes < OUTBUFSIZE)
990d5acd74SJohn Marino 				(void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes,
1000d5acd74SJohn Marino 				    stdout);
1010d5acd74SJohn Marino 			if (ret == (size_t)-1 && errno != E2BIG) {
102af0d08c8SJoerg Sonnenberger 				if (errno != EINVAL || in == inbuf)
103af0d08c8SJoerg Sonnenberger 					err(EXIT_FAILURE, "iconv()");
104af0d08c8SJoerg Sonnenberger 
105af0d08c8SJoerg Sonnenberger 				/* incomplete input character */
1060d5acd74SJohn Marino 				(void)memmove(inbuf, in, inbytes);
107af0d08c8SJoerg Sonnenberger 				ret = fread(inbuf + inbytes, 1,
108af0d08c8SJoerg Sonnenberger 				    INBUFSIZE - inbytes, fp);
109af0d08c8SJoerg Sonnenberger 				if (ret == 0) {
1100d5acd74SJohn Marino 					fflush(stdout);
111af0d08c8SJoerg Sonnenberger 					if (feof(fp))
112af0d08c8SJoerg Sonnenberger 						errx(EXIT_FAILURE,
1130d5acd74SJohn Marino 						    "unexpected end of file; "
1140d5acd74SJohn Marino 						    "the last character is "
1150d5acd74SJohn Marino 						    "incomplete.");
116af0d08c8SJoerg Sonnenberger 					else
117af0d08c8SJoerg Sonnenberger 						err(EXIT_FAILURE, "fread()");
118af0d08c8SJoerg Sonnenberger 				}
119af0d08c8SJoerg Sonnenberger 				in = inbuf;
120af0d08c8SJoerg Sonnenberger 				inbytes += ret;
121af0d08c8SJoerg Sonnenberger 			}
122af0d08c8SJoerg Sonnenberger 		}
123af0d08c8SJoerg Sonnenberger 	}
124af0d08c8SJoerg Sonnenberger 	/* reset the shift state of the output buffer */
125af0d08c8SJoerg Sonnenberger 	outbytes = OUTBUFSIZE;
126af0d08c8SJoerg Sonnenberger 	out = outbuf;
127af0d08c8SJoerg Sonnenberger 	ret = iconv(cd, NULL, NULL, &out, &outbytes);
1280d5acd74SJohn Marino 	if (ret == (size_t)-1)
129af0d08c8SJoerg Sonnenberger 		err(EXIT_FAILURE, "iconv()");
130af0d08c8SJoerg Sonnenberger 	if (outbytes < OUTBUFSIZE)
1310d5acd74SJohn Marino 		(void)fwrite(outbuf, 1, OUTBUFSIZE - outbytes, stdout);
132af0d08c8SJoerg Sonnenberger 
133af0d08c8SJoerg Sonnenberger 	if (invalids > 0 && !silent)
1340d5acd74SJohn Marino 		warnx("warning: invalid characters: %llu", invalids);
135af0d08c8SJoerg Sonnenberger 
136af0d08c8SJoerg Sonnenberger 	iconv_close(cd);
137af0d08c8SJoerg Sonnenberger }
138af0d08c8SJoerg Sonnenberger 
1390d5acd74SJohn Marino static int
do_list(unsigned int n,const char * const * list,void * data __unused)1400d5acd74SJohn Marino do_list(unsigned int n, const char * const *list, void *data __unused)
1410d5acd74SJohn Marino {
1420d5acd74SJohn Marino 	unsigned int i;
1430d5acd74SJohn Marino 
1440d5acd74SJohn Marino 	for(i = 0; i < n; i++) {
1450d5acd74SJohn Marino 		printf("%s", list[i]);
1460d5acd74SJohn Marino 		if (i < n - 1)
1470d5acd74SJohn Marino 			printf(" ");
1480d5acd74SJohn Marino 	}
1490d5acd74SJohn Marino 	printf("\n");
1500d5acd74SJohn Marino 
1510d5acd74SJohn Marino 	return (1);
1520d5acd74SJohn Marino }
1530d5acd74SJohn Marino 
154af0d08c8SJoerg Sonnenberger int
main(int argc,char ** argv)155af0d08c8SJoerg Sonnenberger main(int argc, char **argv)
156af0d08c8SJoerg Sonnenberger {
157af0d08c8SJoerg Sonnenberger 	FILE *fp;
1580d5acd74SJohn Marino 	char *opt_f, *opt_t;
1590d5acd74SJohn Marino 	int ch, i;
1600d5acd74SJohn Marino 	bool opt_c = false, opt_s = false;
161af0d08c8SJoerg Sonnenberger 
1620d5acd74SJohn Marino 	opt_f = opt_t = strdup("");
1630d5acd74SJohn Marino 
1640d5acd74SJohn Marino 	setlocale(LC_ALL, "");
1650d5acd74SJohn Marino 	setprogname(argv[0]);
1660d5acd74SJohn Marino 
1670d5acd74SJohn Marino 	while ((ch = getopt_long(argc, argv, "csLlf:t:",
1680d5acd74SJohn Marino 	    long_options, NULL)) != -1) {
169af0d08c8SJoerg Sonnenberger 		switch (ch) {
170af0d08c8SJoerg Sonnenberger 		case 'c':
1710d5acd74SJohn Marino 			opt_c = true;
172af0d08c8SJoerg Sonnenberger 			break;
173af0d08c8SJoerg Sonnenberger 		case 's':
1740d5acd74SJohn Marino 			opt_s = true;
175af0d08c8SJoerg Sonnenberger 			break;
176af0d08c8SJoerg Sonnenberger 		case 'l':
177af0d08c8SJoerg Sonnenberger 			/* list */
1780d5acd74SJohn Marino 			if (opt_s || opt_c || strcmp(opt_f, "") != 0 ||
1790d5acd74SJohn Marino 			    strcmp(opt_t, "") != 0) {
1800d5acd74SJohn Marino 				warnx("-l is not allowed with other flags.");
1810d5acd74SJohn Marino 				usage();
1820d5acd74SJohn Marino 			}
1830d5acd74SJohn Marino 			iconvlist(do_list, NULL);
1840d5acd74SJohn Marino 			return (EXIT_SUCCESS);
185af0d08c8SJoerg Sonnenberger 		case 'f':
186af0d08c8SJoerg Sonnenberger 			/* from */
1870d5acd74SJohn Marino 			if (optarg != NULL)
188af0d08c8SJoerg Sonnenberger 				opt_f = strdup(optarg);
189af0d08c8SJoerg Sonnenberger 			break;
190af0d08c8SJoerg Sonnenberger 		case 't':
191af0d08c8SJoerg Sonnenberger 			/* to */
1920d5acd74SJohn Marino 			if (optarg != NULL)
193af0d08c8SJoerg Sonnenberger 				opt_t = strdup(optarg);
194af0d08c8SJoerg Sonnenberger 			break;
195af0d08c8SJoerg Sonnenberger 		default:
196af0d08c8SJoerg Sonnenberger 			usage();
197af0d08c8SJoerg Sonnenberger 		}
198af0d08c8SJoerg Sonnenberger 	}
199af0d08c8SJoerg Sonnenberger 	argc -= optind;
200af0d08c8SJoerg Sonnenberger 	argv += optind;
2010d5acd74SJohn Marino 	if ((strcmp(opt_f, "") == 0) && (strcmp(opt_t, "") == 0))
202af0d08c8SJoerg Sonnenberger 		usage();
203af0d08c8SJoerg Sonnenberger 	if (argc == 0)
204af0d08c8SJoerg Sonnenberger 		do_conv(stdin, opt_f, opt_t, opt_s, opt_c);
205af0d08c8SJoerg Sonnenberger 	else {
206af0d08c8SJoerg Sonnenberger 		for (i = 0; i < argc; i++) {
2070d5acd74SJohn Marino 			fp = (strcmp(argv[i], "-") != 0) ?
2080d5acd74SJohn Marino 			    fopen(argv[i], "r") : stdin;
209af0d08c8SJoerg Sonnenberger 			if (fp == NULL)
2100d5acd74SJohn Marino 				err(EXIT_FAILURE, "Cannot open `%s'",
2110d5acd74SJohn Marino 				    argv[i]);
2120d5acd74SJohn Marino 			do_conv(fp, opt_f, opt_t, opt_s,
2130d5acd74SJohn Marino 			    opt_c);
2140d5acd74SJohn Marino 			(void)fclose(fp);
215af0d08c8SJoerg Sonnenberger 		}
216af0d08c8SJoerg Sonnenberger 	}
217af0d08c8SJoerg Sonnenberger 	return (EXIT_SUCCESS);
218af0d08c8SJoerg Sonnenberger }
219