Add iconv test utility
[ira/wip.git] / source / torture / smbiconv.c
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
26 static int process_block (smb_iconv_t cd, const char *addr, size_t len, FILE *output)
27 {
28 #define OUTBUF_SIZE     32768
29   const char *start = addr;
30   char outbuf[OUTBUF_SIZE];
31   char *outptr;
32   size_t outlen;
33   size_t n;
34
35   while (len > 0)
36     {
37       outptr = outbuf;
38       outlen = OUTBUF_SIZE;
39       n = smb_iconv (cd,  &addr, &len, &outptr, &outlen);
40
41       if (outptr != outbuf)
42         {
43           /* We have something to write out.  */
44           int errno_save = errno;
45
46           if (fwrite (outbuf, 1, outptr - outbuf, output)
47               < (size_t) (outptr - outbuf)
48               || ferror (output))
49             {
50               /* Error occurred while printing the result.  */
51               DEBUG (0, ("conversion stopped due to problem in writing the output"));
52               return -1;
53             }
54
55           errno = errno_save;
56         }
57
58       if (errno != E2BIG)
59         {
60           /* iconv() ran into a problem.  */
61           switch (errno)
62             {
63             case EILSEQ:
64               DEBUG(0,("illegal input sequence at position %ld", 
65                      (long) (addr - start)));
66               break;
67             case EINVAL:
68               DEBUG(0, ("\
69 incomplete character or shift sequence at end of buffer"));
70               break;
71             case EBADF:
72               DEBUG(0, ("internal error (illegal descriptor)"));
73               break;
74             default:
75               DEBUG(0, ("unknown iconv() error %d", errno));
76               break;
77             }
78
79           return -1;
80         }
81     }
82
83   return 0;
84 }
85
86
87 static int process_fd (iconv_t cd, int fd, FILE *output)
88 {
89   /* we have a problem with reading from a descriptor since we must not
90      provide the iconv() function an incomplete character or shift
91      sequence at the end of the buffer.  Since we have to deal with
92      arbitrary encodings we must read the whole text in a buffer and
93      process it in one step.  */
94   static char *inbuf = NULL;
95   static size_t maxlen = 0;
96   char *inptr = NULL;
97   size_t actlen = 0;
98
99   while (actlen < maxlen)
100     {
101       ssize_t n = read (fd, inptr, maxlen - actlen);
102
103       if (n == 0)
104         /* No more text to read.  */
105         break;
106
107       if (n == -1)
108         {
109           /* Error while reading.  */
110           DEBUG(0, ("error while reading the input"));
111           return -1;
112         }
113
114       inptr += n;
115       actlen += n;
116     }
117
118   if (actlen == maxlen)
119     while (1)
120       {
121         ssize_t n;
122         char *new_inbuf;
123
124         /* Increase the buffer.  */
125         new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
126         if (new_inbuf == NULL)
127           {
128             DEBUG(0, ("unable to allocate buffer for input"));
129             return -1;
130           }
131         inbuf = new_inbuf;
132         maxlen += 32768;
133         inptr = inbuf + actlen;
134
135         do
136           {
137             n = read (fd, inptr, maxlen - actlen);
138
139             if (n == 0)
140               /* No more text to read.  */
141               break;
142
143             if (n == -1)
144               {
145                 /* Error while reading.  */
146                 DEBUG(0, ("error while reading the input"));
147                 return -1;
148               }
149
150             inptr += n;
151             actlen += n;
152           }
153         while (actlen < maxlen);
154
155         if (n == 0)
156           /* Break again so we leave both loops.  */
157           break;
158       }
159
160   /* Now we have all the input in the buffer.  Process it in one run.  */
161   return process_block (cd, inbuf, actlen, output);
162 }
163
164 /* Main function */
165
166 int main(int argc, char *argv[])
167 {
168         const char *file = NULL;
169         char *from = "";
170         char *to = "";
171         char *output = NULL;
172         const char *preload_modules[] = {NULL, NULL};
173         FILE *out = stdout;
174         int fd;
175         smb_iconv_t cd;
176
177         /* make sure the vars that get altered (4th field) are in
178            a fixed location or certain compilers complain */
179         poptContext pc;
180         struct poptOption long_options[] = {
181                 POPT_AUTOHELP
182                 { "from-code", 'f', POPT_ARG_STRING, &from, 0, "Encoding of original text" },
183                 { "to-code", 't', POPT_ARG_STRING, &to, 0, "Encoding for output" },
184                 { "output", 'o', POPT_ARG_STRING, &output, 0, "Write output to this file" },
185                 { "preload-modules", 'p', POPT_ARG_STRING, &preload_modules[0], 0, "Modules to load" },
186                 POPT_COMMON_SAMBA
187                 POPT_TABLEEND
188         };
189
190         setlinebuf(stdout);
191
192         pc = poptGetContext("smbiconv", argc, (const char **) argv,
193                             long_options, 0);
194
195         poptSetOtherOptionHelp(pc, "[FILE] ...");
196         
197         while(poptGetNextOpt(pc) != -1);
198
199         /* the following functions are part of the Samba debugging
200            facilities.  See lib/debug.c */
201         setup_logging("smbiconv", True);
202
203         if (preload_modules[0]) smb_load_modules(preload_modules);
204
205         if(output) {
206                 out = fopen(output, "w");
207
208                 if(!out) {
209                         DEBUG(0, ("Can't open output file '%s': %s, exiting...\n", output, strerror(errno)));
210                         return 1;
211                 }
212         }
213
214         cd = smb_iconv_open(to, from);
215         if((int)cd == -1) {
216                 DEBUG(0,("unable to find from or to encoding, exiting...\n"));
217                 return 1;
218         }
219
220         while((file = poptGetArg(pc))) {
221                 if(strcmp(file, "-") == 0) fd = 0;
222                 else {
223                         fd = open(file, O_RDONLY);
224                         
225                         if(!fd) {
226                                 DEBUG(0, ("Can't open input file '%s': %s, ignoring...\n", file, strerror(errno)));
227                                 continue;
228                         }
229                 }
230
231                 /* Loop thru all arguments */
232                 process_fd(cd, fd, out);
233
234                 close(fd);
235         }
236         poptFreeContext(pc);
237
238         fclose(out);
239
240         return 0;
241 }