1/*
2   Unix SMB/CIFS implementation.
3   Charset module tester
4
5   Copyright (C) Jelmer Vernooij 2003
6   Based on iconv/icon_prog.c from the GNU C Library,
7      Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22*/
23
24#include "includes.h"
25
26static int
27process_block (smb_iconv_t cd, const char *addr, size_t len, FILE *output)
28{
29#define OUTBUF_SIZE	32768
30  const char *start = addr;
31  char outbuf[OUTBUF_SIZE];
32  char *outptr;
33  size_t outlen;
34  size_t n;
35
36  while (len > 0)
37    {
38      outptr = outbuf;
39      outlen = OUTBUF_SIZE;
40      n = smb_iconv (cd,  &addr, &len, &outptr, &outlen);
41
42      if (outptr != outbuf)
43	{
44	  /* We have something to write out.  */
45	  int errno_save = errno;
46
47	  if (fwrite (outbuf, 1, outptr - outbuf, output)
48	      < (size_t) (outptr - outbuf)
49	      || ferror (output))
50	    {
51	      /* Error occurred while printing the result.  */
52	      DEBUG (0, ("conversion stopped due to problem in writing the output"));
53	      return -1;
54	    }
55
56	  errno = errno_save;
57	}
58
59      if (errno != E2BIG)
60	{
61	  /* iconv() ran into a problem.  */
62	  switch (errno)
63	    {
64	    case EILSEQ:
65	      DEBUG(0,("illegal input sequence at position %ld",
66		     (long) (addr - start)));
67	      break;
68	    case EINVAL:
69	      DEBUG(0, ("\
70incomplete character or shift sequence at end of buffer"));
71	      break;
72	    case EBADF:
73	      DEBUG(0, ("internal error (illegal descriptor)"));
74	      break;
75	    default:
76	      DEBUG(0, ("unknown iconv() error %d", errno));
77	      break;
78	    }
79
80	  return -1;
81	}
82    }
83
84  return 0;
85}
86
87
88static int
89process_fd (iconv_t cd, int fd, FILE *output)
90{
91  /* we have a problem with reading from a descriptor since we must not
92     provide the iconv() function an incomplete character or shift
93     sequence at the end of the buffer.  Since we have to deal with
94     arbitrary encodings we must read the whole text in a buffer and
95     process it in one step.  */
96  static char *inbuf = NULL;
97  static size_t maxlen = 0;
98  char *inptr = NULL;
99  size_t actlen = 0;
100
101  while (actlen < maxlen)
102    {
103      ssize_t n = read (fd, inptr, maxlen - actlen);
104
105      if (n == 0)
106	/* No more text to read.  */
107	break;
108
109      if (n == -1)
110	{
111	  /* Error while reading.  */
112	  DEBUG(0, ("error while reading the input"));
113	  return -1;
114	}
115
116      inptr += n;
117      actlen += n;
118    }
119
120  if (actlen == maxlen)
121    while (1)
122      {
123	ssize_t n;
124	char *new_inbuf;
125
126	/* Increase the buffer.  */
127	new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
128	if (new_inbuf == NULL)
129	  {
130	    DEBUG(0, ("unable to allocate buffer for input"));
131	    return -1;
132	  }
133	inbuf = new_inbuf;
134	maxlen += 32768;
135	inptr = inbuf + actlen;
136
137	do
138	  {
139	    n = read (fd, inptr, maxlen - actlen);
140
141	    if (n == 0)
142	      /* No more text to read.  */
143	      break;
144
145	    if (n == -1)
146	      {
147		/* Error while reading.  */
148		DEBUG(0, ("error while reading the input"));
149		return -1;
150	      }
151
152	    inptr += n;
153	    actlen += n;
154	  }
155	while (actlen < maxlen);
156
157	if (n == 0)
158	  /* Break again so we leave both loops.  */
159	  break;
160      }
161
162  /* Now we have all the input in the buffer.  Process it in one run.  */
163  return process_block (cd, inbuf, actlen, output);
164}
165
166/* Main function */
167
168int main(int argc, char *argv[])
169{
170	const char *file = NULL;
171	char *from = "";
172	char *to = "";
173	char *output = NULL;
174	const char *preload_modules[] = {NULL, NULL};
175	FILE *out = stdout;
176	int fd;
177	smb_iconv_t cd;
178
179	/* make sure the vars that get altered (4th field) are in
180	   a fixed location or certain compilers complain */
181	poptContext pc;
182	struct poptOption long_options[] = {
183		POPT_AUTOHELP
184		{ "from-code", 'f', POPT_ARG_STRING, &from, 0, "Encoding of original text" },
185		{ "to-code", 't', POPT_ARG_STRING, &to, 0, "Encoding for output" },
186		{ "output", 'o', POPT_ARG_STRING, &output, 0, "Write output to this file" },
187		{ "preload-modules", 'p', POPT_ARG_STRING, &preload_modules[0], 0, "Modules to load" },
188		POPT_COMMON_SAMBA
189		POPT_TABLEEND
190	};
191
192	setlinebuf(stdout);
193
194	pc = poptGetContext("smbiconv", argc, (const char **) argv,
195			    long_options, 0);
196
197	poptSetOtherOptionHelp(pc, "[FILE] ...");
198
199	while(poptGetNextOpt(pc) != -1);
200
201	/* the following functions are part of the Samba debugging
202	   facilities.  See lib/debug.c */
203	setup_logging("smbiconv", True);
204
205	if (preload_modules[0]) smb_load_modules(preload_modules);
206
207	if(output) {
208		out = fopen(output, "w");
209
210		if(!out) {
211			DEBUG(0, ("Can't open output file '%s': %s, exiting...\n", output, strerror(errno)));
212			return 1;
213		}
214	}
215
216	cd = smb_iconv_open(to, from);
217	if((int)cd == -1) {
218		DEBUG(0,("unable to find from or to encoding, exiting...\n"));
219		return 1;
220	}
221
222	while((file = poptGetArg(pc))) {
223		if(strcmp(file, "-") == 0) fd = 0;
224		else {
225			fd = open(file, O_RDONLY);
226
227			if(!fd) {
228				DEBUG(0, ("Can't open input file '%s': %s, ignoring...\n", file, strerror(errno)));
229				continue;
230			}
231		}
232
233		/* Loop thru all arguments */
234		process_fd(cd, fd, out);
235
236		close(fd);
237	}
238	poptFreeContext(pc);
239
240	fclose(out);
241
242	return 0;
243}
244