Project

General

Profile

Bug #30 » iconv.c

Gordon Ross, 2011-01-17 07:18 PM

 
1
/*
2
 * This file and its contents are supplied under the terms of the
3
 * Common Development and Distribution License ("CDDL"), version 1.0.
4
 * You may only use this file in accordance with the terms of version
5
 * 1.0 of the CDDL.
6
 *
7
 * A full copy of the text of the CDDL should have accompanied this
8
 * source.  A copy of the CDDL is also available via the Internet at
9
 * http://www.illumos.org/license/CDDL.
10
 */
11

    
12
/*
13
 * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
14
 */
15

    
16
/*
17
 * iconv(1) command.
18
 */
19

    
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <string.h>
23
#include <iconv.h>
24
#include <libintl.h>
25
#include <langinfo.h>
26
#include <locale.h>
27

    
28
const char *progname = "iconv";
29

    
30
char *from_cs;
31
char *to_cs;
32
iconv_t ich;	/* iconv(3c) lib handle */
33
int cflag;	/* skip invalid characters */
34
int sflag;	/* silent */
35
int lflag;	/* list conversions */
36
int errcnt;
37

    
38
void do_iconv(FILE *, const char *);
39
void list_codesets(void);
40

    
41
void
42
usage(void)
43
{
44
	fprintf(stderr, gettext(
45
	    "usage: %s [-cs] [-f from-codeset] [-t to-codeset] "
46
	    "[file ...]\n"), progname);
47
	fprintf(stderr, gettext("\t%s -l\n"), progname);
48
	exit(1);
49
}
50

    
51

    
52
int
53
main(int argc, char **argv)
54
{
55
	char *fname;
56
	FILE *fp;
57
	int c;
58

    
59
	(void) setlocale(LC_ALL, "");
60

    
61
#if !defined(TEXT_DOMAIN)
62
#define	TEXT_DOMAIN	"SYS_TEST"
63
#endif
64
	(void) textdomain(TEXT_DOMAIN);
65

    
66
	while ((c = getopt(argc, argv, "clsf:t:")) != EOF) {
67
		switch (c) {
68
		case 'c':
69
			cflag++;
70
			break;
71
		case 'l':
72
			lflag++;
73
			break;
74
		case 's':
75
			sflag++;
76
			break;
77
		case 'f':
78
			from_cs = optarg;
79
			break;
80
		case 't':
81
			to_cs = optarg;
82
			break;
83
		case '?':
84
			usage();
85
		}
86
	}
87

    
88
	if (lflag) {
89
		if (from_cs != NULL || to_cs != NULL || optind != argc)
90
			usage();
91
		list_codesets();
92
		exit(0);
93
	}
94

    
95
	if (from_cs == NULL)
96
		from_cs = nl_langinfo(CODESET);
97
	if (to_cs == NULL)
98
		to_cs = nl_langinfo(CODESET);
99

    
100
	/*
101
	 * XXX todo: deal with charmap files (/paths)
102
	 */
103

    
104
	ich = iconv_open(to_cs, from_cs);
105
	if (ich == ((iconv_t)-1)) {
106
		fprintf(stderr, "iconv_open failed\n");
107
		exit(1);
108
	}
109

    
110
	if (optind == argc || optind == argc - 1 &&
111
	    0 == strcmp(argv[optind], "-")) {
112
		do_iconv(stdin, "stdin");
113
		exit(0);
114
	}
115

    
116
	for (; optind < argc; optind++) {
117
		fp = fopen(argv[optind], "r");
118
		if (fp == NULL) {
119
			perror(argv[optind]);
120
			exit(1);
121
		}
122
		do_iconv(fp, argv[optind]);
123
		(void) fclose(fp);
124
	}
125
	exit(0);
126
}
127

    
128
/*
129
 * Do actual conversion, copying *fp to stdout.
130
 *
131
 * Conversions may grow or shrink data, so using a larger output buffer
132
 * to reduce the likelihood of leftover input buffer data in each pass.
133
 */
134

    
135
#define	IBUFSIZ	1024
136
#define	OBUFSIZ	(2*IBUFSIZ)
137

    
138
void
139
do_iconv(FILE *fp, const char *fname)
140
{
141
	char ibuf[IBUFSIZ];
142
	char obuf[OBUFSIZ];
143
	const char *iptr;
144
	char *optr;
145
	size_t ileft, icnt, oleft, ocnt;
146
	int nr, nw, rc;
147

    
148

    
149
	while ((nr = fread(ibuf, 1, IBUFSIZ, fp)) > 0) {
150

    
151
		iptr = ibuf;
152
		ileft = nr;
153

    
154
		while (ileft > 0) {
155
			optr = obuf;
156
			oleft = OBUFSIZ;
157
			rc = iconv(ich, &iptr, &ileft, &optr, &oleft);
158
			if (rc == (size_t)-1) {
159
				/*
160
				 * XXX todo: deal with skipping invalid
161
				 * input characters and continue...
162
				 */
163
				errcnt++;
164
				break;
165
			}
166
			ocnt = OBUFSIZ - oleft;
167
			nw = fwrite(obuf, 1, ocnt, stdout);
168
			if (nw != ocnt) {
169
				perror("write");
170
				exit(1);
171
			}
172
		}
173
	}
174

    
175
	/*
176
	 * End of file
177
	 * Flush any shift encodings.
178
	 */
179
	iptr = NULL;
180
	ileft = 0;
181
	optr = obuf;
182
	oleft = OBUFSIZ;
183
	iconv(ich, &iptr, &ileft, &optr, &oleft);
184
	ocnt = OBUFSIZ - oleft;
185
	fwrite(obuf, 1, ocnt, stdout);
186
}
187

    
188
/*
189
 * XXX todo: scan the /usr/lib/iconv directory...
190
 * Firing off system(3c) of a ksh script here
191
 * might be appropriate.  see iconv-l.ksh
192
 */
193
void
194
list_codesets(void)
195
{
196

    
197
}
(1-1/2)