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