6b34d3460b007da951c83ce2eee975a62c72801d
[kopensolaris-gnu/glibc.git] / iconv / iconv_prog.c
1 /* Convert text in given files from the specified from-set to the to-set.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <argp.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <error.h>
25 #include <fcntl.h>
26 #include <gconv.h>
27 #include <iconv.h>
28 #include <locale.h>
29 #include <search.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/mman.h>
35
36 /* Get libc version number.  */
37 #include "../version.h"
38
39 #define PACKAGE _libc_intl_domainname
40
41
42 /* Name and version of program.  */
43 static void print_version (FILE *stream, struct argp_state *state);
44 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
45
46 #define OPT_VERBOSE     1000
47 #define OPT_LIST        1001
48
49 /* Definitions of arguments for argp functions.  */
50 static const struct argp_option options[] =
51 {
52   { NULL, 0, NULL, 0, N_("Input/Output format specification:") },
53   { "from-code", 'f', "NAME", 0, N_("encoding of original text") },
54   { "to-code", 't', "NAME", 0, N_("encoding for output") },
55   { NULL, 0, NULL, 0, N_("Information:") },
56   { "list", OPT_LIST, NULL, 0, N_("list all known coded character sets") },
57   { NULL, 0, NULL, 0, N_("Output control:") },
58   { "output", 'o', "FILE", 0, N_("output file") },
59   { "verbose", OPT_VERBOSE, NULL, 0, N_("print progress information") },
60   { NULL, 0, NULL, 0, NULL }
61 };
62
63 /* Short description of program.  */
64 static const char doc[] = N_("\
65 Convert encoding of given files from one encoding to another.");
66
67 /* Strings for arguments in help texts.  */
68 static const char args_doc[] = N_("[FILE...]");
69
70 /* Prototype for option handler.  */
71 static error_t parse_opt __P ((int key, char *arg, struct argp_state *state));
72
73 /* Function to print some extra text in the help message.  */
74 static char *more_help __P ((int key, const char *text, void *input));
75
76 /* Data structure to communicate with argp functions.  */
77 static struct argp argp =
78 {
79   options, parse_opt, args_doc, doc, NULL, more_help
80 };
81
82 /* Code sets to convert from and to respectively.  */
83 static const char *from_code;
84 static const char *to_code;
85
86 /* File to write output to.  If NULL write to stdout.  */
87 static const char *output_file;
88
89 /* Nonzero if verbose ouput is wanted.  */
90 static int verbose;
91
92 /* Nonzero if list of all coded character sets is wanted.  */
93 static int list;
94
95 /* Prototypes for the functions doing the actual work.  */
96 static int process_block (iconv_t cd, const char *addr, size_t len,
97                           FILE *output);
98 static int process_fd (iconv_t cd, int fd, FILE *output);
99 static int process_file (iconv_t cd, FILE *input, FILE *output);
100 static void print_known_names (void);
101
102
103 int
104 main (int argc, char *argv[])
105 {
106   int status = EXIT_SUCCESS;
107   int remaining;
108   FILE *output;
109   iconv_t cd;
110
111   /* Set locale via LC_ALL.  */
112   setlocale (LC_ALL, "");
113
114   /* Set the text message domain.  */
115   textdomain (_libc_intl_domainname);
116
117   /* Parse and process arguments.  */
118   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
119
120   /* List all coded character sets if wanted.  */
121   if (list)
122     {
123       print_known_names ();
124       exit (EXIT_SUCCESS);
125     }
126
127   /* If either the from- or to-code is not specified this is an error
128      since we do not know what to do.  */
129   if (from_code == NULL && to_code == NULL)
130     error (EXIT_FAILURE, 0,
131            _("neither original nor target encoding specified"));
132   if (from_code == NULL)
133     error (EXIT_FAILURE, 0, _("original encoding not specified using `-f'"));
134   if (to_code == NULL)
135     error (EXIT_FAILURE, 0, _("target encoding not specified using `-t'"));
136
137   /* Let's see whether we have these coded character sets.  */
138   cd = iconv_open (to_code, from_code);
139   if (cd == (iconv_t) -1)
140     if (errno == EINVAL)
141       error (EXIT_FAILURE, 0, _("conversion from `%s' to `%s' not supported"),
142              from_code, to_code);
143     else
144       error (EXIT_FAILURE, errno, _("failed to start conversion processing"));
145
146   /* Determine output file.  */
147   if (output_file != NULL)
148     {
149       output = fopen (output_file, "w");
150       if (output == NULL)
151         error (EXIT_FAILURE, errno, _("cannot open output file"));
152     }
153   else
154     output = stdout;
155
156   /* Now process the remaining files.  Write them to stdout or the file
157      specified with the `-o' parameter.  If we have no file given as
158      the parameter process all from stdin.  */
159   if (remaining == argc)
160     process_file (cd, stdin, output);
161   else
162     do
163       {
164         struct stat st;
165         const char *addr;
166         int fd = open (argv[remaining], O_RDONLY);
167
168         if (verbose)
169           printf ("%s:\n", argv[remaining]);
170
171         if (fd == -1)
172           {
173             error (0, errno, _("cannot open input file `%s'"),
174                    argv[remaining]);
175             status = EXIT_FAILURE;
176             continue;
177           }
178
179         /* We have possibilities for reading the input file.  First try
180            to mmap() it since this will provide the fastest solution.  */
181         if (fstat (fd, &st) == 0
182             && ((addr = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0))
183                 != MAP_FAILED))
184           {
185             /* Yes, we can use mmap().  The descriptor is not needed
186                anymore.  */
187             if (close (fd) != 0)
188               error (EXIT_FAILURE, errno, _("error while closing input `%s'"),
189                      argv[remaining]);
190
191             if (process_block (cd, addr, st.st_size, stdout) < 0)
192               {
193                 /* Something went wrong.  */
194                 status = EXIT_FAILURE;
195
196                 /* We don't need the input data anymore.  */
197                 munmap ((void *) addr, st.st_size);
198
199                 /* We cannot go on with producing output since it might
200                    lead to problem because the last output might leave
201                    the output stream in an undefined state.  */
202                 break;
203               }
204
205             /* We don't need the input data anymore.  */
206             munmap ((void *) addr, st.st_size);
207           }
208         else
209           {
210             /* Read the file in pieces.  */
211             if (process_fd (cd, fd, output) != 0)
212               {
213                 /* Something went wrong.  */
214                 status = EXIT_FAILURE;
215
216                 /* We don't need the input file anymore.  */
217                 close (fd);
218
219                 /* We cannot go on with producing output since it might
220                    lead to problem because the last output might leave
221                    the output stream in an undefined state.  */
222                 break;
223               }
224
225             /* Now close the file.  */
226             close (fd);
227           }
228       }
229     while (++remaining < argc);
230
231   /* Close the output file now.  */
232   if (fclose (output))
233     error (EXIT_FAILURE, errno, _("error while closing output file"));
234
235   return status;
236 }
237
238
239 /* Handle program arguments.  */
240 static error_t
241 parse_opt (int key, char *arg, struct argp_state *state)
242 {
243   switch (key)
244     {
245     case 'f':
246       from_code = arg;
247       break;
248     case 't':
249       to_code = arg;
250       break;
251     case 'o':
252       output_file = arg;
253       break;
254     case OPT_VERBOSE:
255       verbose = 1;
256       break;
257     case OPT_LIST:
258       list = 1;
259       break;
260     default:
261       return ARGP_ERR_UNKNOWN;
262     }
263   return 0;
264 }
265
266
267 static char *
268 more_help (int key, const char *text, void *input)
269 {
270   switch (key)
271     {
272     case ARGP_KEY_HELP_EXTRA:
273       /* We print some extra information.  */
274       return strdup (gettext ("\
275 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
276     default:
277       break;
278     }
279   return (char *) text;
280 }
281
282
283 /* Print the version information.  */
284 static void
285 print_version (FILE *stream, struct argp_state *state)
286 {
287   fprintf (stream, "iconv (GNU %s) %s\n", PACKAGE, VERSION);
288   fprintf (stream, gettext ("\
289 Copyright (C) %s Free Software Foundation, Inc.\n\
290 This is free software; see the source for copying conditions.  There is NO\n\
291 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
292 "), "1998");
293   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
294 }
295
296
297 static int
298 process_block (iconv_t cd, const char *addr, size_t len, FILE *output)
299 {
300 #define OUTBUF_SIZE     32768
301   char outbuf[OUTBUF_SIZE];
302   char *outptr = outbuf;
303   size_t outlen = OUTBUF_SIZE;
304
305   while (len > 0)
306     {
307       size_t n = iconv (cd, &addr, &len, &outptr, &outlen);
308
309       if (outptr != outbuf)
310         {
311           /* We have something to write out.  */
312           if (fwrite (outbuf, 1, outptr - outbuf, output)  < outptr - outbuf
313               || ferror (output))
314             {
315               /* Error occurred while printing the result.  */
316               error (0, 0, _("\
317 conversion stopped due to problem in writing the output"));
318               return -1;
319             }
320         }
321
322       if (n != (size_t) -1)
323         /* Everything is processed.  */
324         break;
325
326       if (errno != E2BIG)
327         {
328           /* iconv() ran into a problem.  */
329           switch (errno)
330             {
331             case EILSEQ:
332               error (0, 0, _("illegal input sequence"));
333               break;
334             case EINVAL:
335               error (0, 0, _("\
336 incomplete character or shift sequence at end of buffer"));
337               break;
338             case EBADF:
339               error (0, 0, _("internal error (illegal descriptor)"));
340               break;
341             default:
342               error (0, 0, _("unknown iconv() error %d"), errno);
343               break;
344             }
345
346           return -1;
347         }
348     }
349
350   return 0;
351 }
352
353
354 static int
355 process_fd (iconv_t cd, int fd, FILE *output)
356 {
357   /* we have a problem with reading from a desriptor since we must not
358      provide the iconv() function an incomplete character or shift
359      sequence at the end of the buffer.  Since we have to deal with
360      arbitrary encodings we must read the whole text in a buffer and
361      process it in one step.  */
362   static char *inbuf = NULL;
363   static size_t maxlen = 0;
364   char *inptr = NULL;
365   size_t actlen = 0;
366
367   while (actlen < maxlen)
368     {
369       size_t n = read (fd, inptr, maxlen - actlen);
370
371       if (n == 0)
372         /* No more text to read.  */
373         break;
374
375       if (n == -1)
376         {
377           /* Error while reading.  */
378           error (0, errno, _("error while reading the input"));
379           return -1;
380         }
381
382       inptr += n;
383       actlen += n;
384     }
385
386   if (actlen == maxlen)
387     while (1)
388       {
389         size_t n;
390
391         /* Increase the buffer.  */
392         maxlen += 32768;
393         inbuf = realloc (inbuf, maxlen);
394         if (inbuf == NULL)
395           error (0, errno, _("unable to allocate buffer for input"));
396         inptr = inbuf + actlen;
397
398         do
399           {
400             n = read (fd, inptr, maxlen - actlen);
401
402             if (n == 0)
403               /* No more text to read.  */
404               break;
405
406             if (n == -1)
407               {
408                 /* Error while reading.  */
409                 error (0, errno, _("error while reading the input"));
410                 return -1;
411               }
412
413             inptr += n;
414             actlen += n;
415           }
416         while (actlen < maxlen);
417
418         if (n == 0)
419           /* Break again so we leave both loops.  */
420           break;
421       }
422
423   /* Now we have all the input in the buffer.  Process it in one run.  */
424   return process_block (cd, inbuf, actlen, output);
425 }
426
427
428 static int
429 process_file (iconv_t cd, FILE *input, FILE *output)
430 {
431   /* This should be safe since we use this function only for `stdin' and
432      we haven't read anything so far.  */
433   return process_fd (cd, fileno (input), output);
434 }
435
436
437 /* Print all known character sets/encodings.  */
438 static void *printlist;
439 static size_t column;
440 static int not_first;
441
442 static void
443 insert_print_list (const void *nodep, VISIT value, int level)
444 {
445   if (value == leaf || value == postorder)
446     {
447       const struct gconv_alias *s = *(const struct gconv_alias **) nodep;
448       tsearch (s->fromname, &printlist, (__compar_fn_t) strcoll);
449     }
450 }
451
452 static void
453 do_print  (const void *nodep, VISIT value, int level)
454 {
455   if (value == leaf || value == postorder)
456     {
457       const char *s = *(const char **) nodep;
458       size_t len = strlen (s);
459       size_t cnt;
460
461       while (len > 0 && s[len - 1] == '/')
462         --len;
463
464       for (cnt = 0; cnt < len; ++cnt)
465         if (isalnum (s[cnt]))
466           break;
467       if (cnt == len)
468         return;
469
470       if (not_first)
471         {
472           putchar (',');
473           ++column;
474
475           if (column > 2 && column + len > 77)
476             {
477               fputs ("\n  ", stdout);
478               column = 2;
479             }
480           else
481             {
482               putchar (' ');
483               ++column;
484             }
485         }
486       else
487           not_first = 1;
488
489       fwrite (s, len, 1, stdout);
490       column += len;
491     }
492 }
493
494 static void
495 print_known_names (void)
496 {
497   size_t cnt;
498   iconv_t h;
499
500   /* We must initialize the internal databases first.  */
501   h = iconv_open ("L1", "L1");
502   iconv_close (h);
503
504   /* First add the aliases.  */
505   twalk (__gconv_alias_db, insert_print_list);
506
507   /* Add the from- and to-names from the known modules.  */
508   for (cnt = 0; cnt < __gconv_nmodules; ++cnt)
509     {
510       if (__gconv_modules_db[cnt]->from_pattern == NULL)
511         {
512           tsearch (__gconv_modules_db[cnt]->from_constpfx, &printlist,
513                    (__compar_fn_t) strcoll);
514           tsearch (__gconv_modules_db[cnt]->to_string, &printlist,
515                    (__compar_fn_t) strcoll);
516         }
517       else
518         tsearch (__gconv_modules_db[cnt]->from_pattern, &printlist,
519                  (__compar_fn_t) strcoll);
520     }
521
522   fputs (_("\
523 The following list contain all the coded character sets known.  This does\n\
524 not necessarily mean that all combinations of these names can be used for\n\
525 the FROM and TO command line parameters.  One coded character set can be\n\
526 listed with several different names (aliases).\n\
527   Some of the names are no plain strings but instead regular expressions and\n\
528 they match a variety of names which can be given as parameters to the\n\
529 program.\n\n  "), stdout);
530
531   /* Now print the collected names.  */
532   column = 2;
533   twalk (printlist, do_print);
534
535   if (column != 0)
536     puts ("");
537 }