Update.
[platform/upstream/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, 1999, 2000, 2001, 2002 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 Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the 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    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <argp.h>
22 #include <assert.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <error.h>
26 #include <fcntl.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 <libintl.h>
35 #ifdef _POSIX_MAPPED_FILES
36 # include <sys/mman.h>
37 #endif
38 #include <charmap.h>
39 #include <gconv_int.h>
40 #include "iconv_prog.h"
41 #include "iconvconfig.h"
42
43 /* Get libc version number.  */
44 #include "../version.h"
45
46 #define PACKAGE _libc_intl_domainname
47
48
49 /* Defined in gconv_cache.c.  */
50 extern void *__gconv_cache;
51
52 /* Name and version of program.  */
53 static void print_version (FILE *stream, struct argp_state *state);
54 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
55
56 #define OPT_VERBOSE     1000
57 #define OPT_LIST        'l'
58
59 /* Definitions of arguments for argp functions.  */
60 static const struct argp_option options[] =
61 {
62   { NULL, 0, NULL, 0, N_("Input/Output format specification:") },
63   { "from-code", 'f', "NAME", 0, N_("encoding of original text") },
64   { "to-code", 't', "NAME", 0, N_("encoding for output") },
65   { NULL, 0, NULL, 0, N_("Information:") },
66   { "list", 'l', NULL, 0, N_("list all known coded character sets") },
67   { NULL, 0, NULL, 0, N_("Output control:") },
68   { NULL, 'c', NULL, 0, N_("omit invalid characters from output") },
69   { "output", 'o', "FILE", 0, N_("output file") },
70   { "silent", 's', NULL, 0, N_("suppress warnings") },
71   { "verbose", OPT_VERBOSE, NULL, 0, N_("print progress information") },
72   { NULL, 0, NULL, 0, NULL }
73 };
74
75 /* Short description of program.  */
76 static const char doc[] = N_("\
77 Convert encoding of given files from one encoding to another.");
78
79 /* Strings for arguments in help texts.  */
80 static const char args_doc[] = N_("[FILE...]");
81
82 /* Prototype for option handler.  */
83 static error_t parse_opt (int key, char *arg, struct argp_state *state);
84
85 /* Function to print some extra text in the help message.  */
86 static char *more_help (int key, const char *text, void *input);
87
88 /* Data structure to communicate with argp functions.  */
89 static struct argp argp =
90 {
91   options, parse_opt, args_doc, doc, NULL, more_help
92 };
93
94 /* Code sets to convert from and to respectively.  An empty string as the
95    default causes the 'iconv_open' function to look up the charset of the
96    currently selected locale and use it.  */
97 static const char *from_code = "";
98 static const char *to_code = "";
99
100 /* File to write output to.  If NULL write to stdout.  */
101 static const char *output_file;
102
103 /* Nonzero if verbose ouput is wanted.  */
104 int verbose;
105
106 /* Nonzero if list of all coded character sets is wanted.  */
107 static int list;
108
109 /* If nonzero omit invalid character from output.  */
110 int omit_invalid;
111
112 /* Prototypes for the functions doing the actual work.  */
113 static int process_block (iconv_t cd, char *addr, size_t len, FILE *output);
114 static int process_fd (iconv_t cd, int fd, FILE *output);
115 static int process_file (iconv_t cd, FILE *input, FILE *output);
116 static void print_known_names (void) internal_function;
117
118
119 int
120 main (int argc, char *argv[])
121 {
122   int status = EXIT_SUCCESS;
123   int remaining;
124   FILE *output;
125   iconv_t cd;
126   const char *orig_to_code;
127   struct charmap_t *from_charmap = NULL;
128   struct charmap_t *to_charmap = NULL;
129
130   /* Set locale via LC_ALL.  */
131   setlocale (LC_ALL, "");
132
133   /* Set the text message domain.  */
134   textdomain (_libc_intl_domainname);
135
136   /* Parse and process arguments.  */
137   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
138
139   /* List all coded character sets if wanted.  */
140   if (list)
141     {
142       print_known_names ();
143       exit (EXIT_SUCCESS);
144     }
145
146   /* If we have to ignore errors make sure we use the appropriate name for
147      the to-character-set.  */
148   orig_to_code = to_code;
149   if (omit_invalid)
150     {
151       const char *errhand = strchrnul (to_code, '/');
152       int nslash = 2;
153       char *newp;
154       char *cp;
155
156       if (*errhand == '/')
157         {
158           --nslash;
159           errhand = strchrnul (errhand, '/');
160
161           if (*errhand == '/')
162             {
163               --nslash;
164               ++errhand;
165             }
166         }
167
168       newp = (char *) alloca (errhand - to_code + nslash + 6 + 1);
169       cp = mempcpy (newp, to_code, errhand - to_code);
170       while (nslash-- > 0)
171         *cp++ = '/';
172       memcpy (cp, "IGNORE", sizeof ("IGNORE"));
173
174       to_code = newp;
175     }
176
177   /* POSIX 1003.2b introduces a silly thing: the arguments to -t anf -f
178      can be file names of charmaps.  In this case iconv will have to read
179      those charmaps and use them to do the conversion.  But there are
180      holes in the specification.  There is nothing said that if -f is a
181      charmap filename that -t must be, too.  And vice versa.  There is
182      also no word about the symbolic names used.  What if they don't
183      match?  */
184   if (strchr (from_code, '/') != NULL)
185     /* The from-name might be a charmap file name.  Try reading the
186        file.  */
187     from_charmap = charmap_read (from_code, /*0, 1*/1, 0, 0);
188
189   if (strchr (orig_to_code, '/') != NULL)
190     /* The to-name might be a charmap file name.  Try reading the
191        file.  */
192     to_charmap = charmap_read (orig_to_code, /*0, 1,*/1,0, 0);
193
194
195   /* Determine output file.  */
196   if (output_file != NULL && strcmp (output_file, "-") != 0)
197     {
198       output = fopen (output_file, "w");
199       if (output == NULL)
200         error (EXIT_FAILURE, errno, _("cannot open output file"));
201     }
202   else
203     output = stdout;
204
205   /* At this point we have to handle two cases.  The first one is
206      where a charmap is used for the from- or to-charset, or both.  We
207      handle this special since it is very different from the sane way of
208      doing things.  The other case allows converting using the iconv()
209      function.  */
210   if (from_charmap != NULL || to_charmap != NULL)
211     /* Construct the conversion table and do the conversion.  */
212     status = charmap_conversion (from_code, from_charmap, to_code, to_charmap,
213                                  argc, remaining, argv, output);
214   else
215     {
216       /* Let's see whether we have these coded character sets.  */
217       cd = iconv_open (to_code, from_code);
218       if (cd == (iconv_t) -1)
219         {
220           if (errno == EINVAL)
221             error (EXIT_FAILURE, 0,
222                    _("conversion from `%s' to `%s' not supported"),
223                    from_code, orig_to_code);
224           else
225             error (EXIT_FAILURE, errno,
226                    _("failed to start conversion processing"));
227         }
228
229       /* Now process the remaining files.  Write them to stdout or the file
230          specified with the `-o' parameter.  If we have no file given as
231          the parameter process all from stdin.  */
232       if (remaining == argc)
233         {
234           if (process_file (cd, stdin, output) != 0)
235             status = EXIT_FAILURE;
236         }
237       else
238         do
239           {
240 #ifdef _POSIX_MAPPED_FILES
241             struct stat st;
242             char *addr;
243 #endif
244             int fd;
245
246             if (verbose)
247               printf ("%s:\n", argv[remaining]);
248             if (strcmp (argv[remaining], "-") == 0)
249               fd = 0;
250             else
251               {
252                 fd = open (argv[remaining], O_RDONLY);
253
254                 if (fd == -1)
255                   {
256                     error (0, errno, _("cannot open input file `%s'"),
257                            argv[remaining]);
258                     status = EXIT_FAILURE;
259                     continue;
260                   }
261               }
262
263 #ifdef _POSIX_MAPPED_FILES
264             /* We have possibilities for reading the input file.  First try
265                to mmap() it since this will provide the fastest solution.  */
266             if (fstat (fd, &st) == 0
267                 && ((addr = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE,
268                                   fd, 0)) != MAP_FAILED))
269               {
270                 /* Yes, we can use mmap().  The descriptor is not needed
271                    anymore.  */
272                 if (close (fd) != 0)
273                   error (EXIT_FAILURE, errno,
274                          _("error while closing input `%s'"),
275                          argv[remaining]);
276
277                 if (process_block (cd, addr, st.st_size, output) < 0)
278                   {
279                     /* Something went wrong.  */
280                     status = EXIT_FAILURE;
281
282                     /* We don't need the input data anymore.  */
283                     munmap ((void *) addr, st.st_size);
284
285                     /* We cannot go on with producing output since it might
286                        lead to problem because the last output might leave
287                        the output stream in an undefined state.  */
288                     break;
289                   }
290
291                 /* We don't need the input data anymore.  */
292                 munmap ((void *) addr, st.st_size);
293               }
294             else
295 #endif  /* _POSIX_MAPPED_FILES */
296               {
297                 /* Read the file in pieces.  */
298                 if (process_fd (cd, fd, output) != 0)
299                   {
300                     /* Something went wrong.  */
301                     status = EXIT_FAILURE;
302
303                     /* We don't need the input file anymore.  */
304                     close (fd);
305
306                     /* We cannot go on with producing output since it might
307                        lead to problem because the last output might leave
308                        the output stream in an undefined state.  */
309                     break;
310                   }
311
312                 /* Now close the file.  */
313                 close (fd);
314               }
315           }
316         while (++remaining < argc);
317     }
318
319   /* Close the output file now.  */
320   if (fclose (output))
321     error (EXIT_FAILURE, errno, _("error while closing output file"));
322
323   return status;
324 }
325
326
327 /* Handle program arguments.  */
328 static error_t
329 parse_opt (int key, char *arg, struct argp_state *state)
330 {
331   switch (key)
332     {
333     case 'f':
334       from_code = arg;
335       break;
336     case 't':
337       to_code = arg;
338       break;
339     case 'o':
340       output_file = arg;
341       break;
342     case 's':
343       /* Nothing, for now at least.  We are not giving out any information
344          about missing character or so.  */
345       break;
346     case 'c':
347       /* Omit invalid characters from output.  */
348       omit_invalid = 1;
349       break;
350     case OPT_VERBOSE:
351       verbose = 1;
352       break;
353     case OPT_LIST:
354       list = 1;
355       break;
356     default:
357       return ARGP_ERR_UNKNOWN;
358     }
359   return 0;
360 }
361
362
363 static char *
364 more_help (int key, const char *text, void *input)
365 {
366   switch (key)
367     {
368     case ARGP_KEY_HELP_EXTRA:
369       /* We print some extra information.  */
370       return strdup (gettext ("\
371 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"));
372     default:
373       break;
374     }
375   return (char *) text;
376 }
377
378
379 /* Print the version information.  */
380 static void
381 print_version (FILE *stream, struct argp_state *state)
382 {
383   fprintf (stream, "iconv (GNU %s) %s\n", PACKAGE, VERSION);
384   fprintf (stream, gettext ("\
385 Copyright (C) %s Free Software Foundation, Inc.\n\
386 This is free software; see the source for copying conditions.  There is NO\n\
387 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
388 "), "2002");
389   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
390 }
391
392
393 static int
394 process_block (iconv_t cd, char *addr, size_t len, FILE *output)
395 {
396 #define OUTBUF_SIZE     32768
397   const char *start = addr;
398   char outbuf[OUTBUF_SIZE];
399   char *outptr;
400   size_t outlen;
401   size_t n;
402
403   while (len > 0)
404     {
405       outptr = outbuf;
406       outlen = OUTBUF_SIZE;
407       n = iconv (cd, &addr, &len, &outptr, &outlen);
408
409       if (outptr != outbuf)
410         {
411           /* We have something to write out.  */
412           int errno_save = errno;
413
414           if (fwrite (outbuf, 1, outptr - outbuf, output) < outptr - outbuf
415               || ferror (output))
416             {
417               /* Error occurred while printing the result.  */
418               error (0, 0, _("\
419 conversion stopped due to problem in writing the output"));
420               return -1;
421             }
422
423           errno = errno_save;
424         }
425
426       if (n != (size_t) -1)
427         {
428           /* All the input test is processed.  For state-dependent
429              character sets we have to flush the state now.  */
430           outptr = outbuf;
431           outlen = OUTBUF_SIZE;
432           (void) iconv (cd, NULL, NULL, &outptr, &outlen);
433
434           if (outptr != outbuf)
435             {
436               /* We have something to write out.  */
437               int errno_save = errno;
438
439               if (fwrite (outbuf, 1, outptr - outbuf, output) < outptr - outbuf
440                   || ferror (output))
441                 {
442                   /* Error occurred while printing the result.  */
443                   error (0, 0, _("\
444 conversion stopped due to problem in writing the output"));
445                   return -1;
446                 }
447
448               errno = errno_save;
449             }
450
451           break;
452         }
453
454       if (errno != E2BIG)
455         {
456           /* iconv() ran into a problem.  */
457           switch (errno)
458             {
459             case EILSEQ:
460               error (0, 0, _("illegal input sequence at position %ld"),
461                      (long) (addr - start));
462               break;
463             case EINVAL:
464               error (0, 0, _("\
465 incomplete character or shift sequence at end of buffer"));
466               break;
467             case EBADF:
468               error (0, 0, _("internal error (illegal descriptor)"));
469               break;
470             default:
471               error (0, 0, _("unknown iconv() error %d"), errno);
472               break;
473             }
474
475           return -1;
476         }
477     }
478
479   return 0;
480 }
481
482
483 static int
484 process_fd (iconv_t cd, int fd, FILE *output)
485 {
486   /* we have a problem with reading from a desriptor since we must not
487      provide the iconv() function an incomplete character or shift
488      sequence at the end of the buffer.  Since we have to deal with
489      arbitrary encodings we must read the whole text in a buffer and
490      process it in one step.  */
491   static char *inbuf = NULL;
492   static size_t maxlen = 0;
493   char *inptr = NULL;
494   size_t actlen = 0;
495
496   while (actlen < maxlen)
497     {
498       ssize_t n = read (fd, inptr, maxlen - actlen);
499
500       if (n == 0)
501         /* No more text to read.  */
502         break;
503
504       if (n == -1)
505         {
506           /* Error while reading.  */
507           error (0, errno, _("error while reading the input"));
508           return -1;
509         }
510
511       inptr += n;
512       actlen += n;
513     }
514
515   if (actlen == maxlen)
516     while (1)
517       {
518         ssize_t n;
519         char *new_inbuf;
520
521         /* Increase the buffer.  */
522         new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
523         if (new_inbuf == NULL)
524           {
525             error (0, errno, _("unable to allocate buffer for input"));
526             return -1;
527           }
528         inbuf = new_inbuf;
529         maxlen += 32768;
530         inptr = inbuf + actlen;
531
532         do
533           {
534             n = read (fd, inptr, maxlen - actlen);
535
536             if (n == 0)
537               /* No more text to read.  */
538               break;
539
540             if (n == -1)
541               {
542                 /* Error while reading.  */
543                 error (0, errno, _("error while reading the input"));
544                 return -1;
545               }
546
547             inptr += n;
548             actlen += n;
549           }
550         while (actlen < maxlen);
551
552         if (n == 0)
553           /* Break again so we leave both loops.  */
554           break;
555       }
556
557   /* Now we have all the input in the buffer.  Process it in one run.  */
558   return process_block (cd, inbuf, actlen, output);
559 }
560
561
562 static int
563 process_file (iconv_t cd, FILE *input, FILE *output)
564 {
565   /* This should be safe since we use this function only for `stdin' and
566      we haven't read anything so far.  */
567   return process_fd (cd, fileno (input), output);
568 }
569
570
571 /* Print all known character sets/encodings.  */
572 static void *printlist;
573 static size_t column;
574 static int not_first;
575
576 static void
577 insert_print_list (const void *nodep, VISIT value, int level)
578 {
579   if (value == leaf || value == postorder)
580     {
581       const struct gconv_alias *s = *(const struct gconv_alias **) nodep;
582       tsearch (s->fromname, &printlist, (__compar_fn_t) strverscmp);
583     }
584 }
585
586 static void
587 do_print_human  (const void *nodep, VISIT value, int level)
588 {
589   if (value == leaf || value == postorder)
590     {
591       const char *s = *(const char **) nodep;
592       size_t len = strlen (s);
593       size_t cnt;
594
595       while (len > 0 && s[len - 1] == '/')
596         --len;
597
598       for (cnt = 0; cnt < len; ++cnt)
599         if (isalnum (s[cnt]))
600           break;
601       if (cnt == len)
602         return;
603
604       if (not_first)
605         {
606           putchar (',');
607           ++column;
608
609           if (column > 2 && column + len > 77)
610             {
611               fputs ("\n  ", stdout);
612               column = 2;
613             }
614           else
615             {
616               putchar (' ');
617               ++column;
618             }
619         }
620       else
621         not_first = 1;
622
623       fwrite (s, len, 1, stdout);
624       column += len;
625     }
626 }
627
628 static void
629 do_print  (const void *nodep, VISIT value, int level)
630 {
631   if (value == leaf || value == postorder)
632     {
633       const char *s = *(const char **) nodep;
634
635       puts (s);
636     }
637 }
638
639 static void
640 internal_function
641 add_known_names (struct gconv_module *node)
642 {
643   if (node->left != NULL)
644     add_known_names (node->left);
645   if (node->right != NULL)
646     add_known_names (node->right);
647   do
648     {
649       if (strcmp (node->from_string, "INTERNAL"))
650         tsearch (node->from_string, &printlist,
651                  (__compar_fn_t) strverscmp);
652       if (strcmp (node->to_string, "INTERNAL") != 0)
653         tsearch (node->to_string, &printlist, (__compar_fn_t) strverscmp);
654
655       node = node->same;
656     }
657   while (node != NULL);
658 }
659
660
661 static void
662 insert_cache (void)
663 {
664   const struct gconvcache_header *header;
665   const char *strtab;
666   const struct hash_entry *hashtab;
667   size_t cnt;
668
669   header = (const struct gconvcache_header *) __gconv_cache;
670   strtab = (char *) __gconv_cache + header->string_offset;
671   hashtab = (struct hash_entry *) ((char *) __gconv_cache
672                                    + header->hash_offset);
673
674   for (cnt = 0; cnt < header->hash_size; ++cnt)
675     if (hashtab[cnt].string_offset != 0)
676       {
677         const char *str = strtab + hashtab[cnt].string_offset;
678
679         if (strcmp (str, "INTERNAL") != 0)
680           tsearch (str, &printlist, (__compar_fn_t) strverscmp);
681       }
682 }
683
684
685 static void
686 internal_function
687 print_known_names (void)
688 {
689   iconv_t h;
690
691   /* We must initialize the internal databases first.  */
692   h = iconv_open ("L1", "L1");
693   iconv_close (h);
694
695   /* See whether we have a cache.  */
696   if (__gconv_cache != NULL)
697     /* Yep, use only this information.  */
698     insert_cache ();
699   else
700     {
701       /* No, then use the information read from the gconv-modules file.
702          First add the aliases.  */
703       twalk (__gconv_alias_db, insert_print_list);
704
705       /* Add the from- and to-names from the known modules.  */
706       if (__gconv_modules_db != NULL)
707         add_known_names (__gconv_modules_db);
708     }
709
710   fputs (_("\
711 The following list contain all the coded character sets known.  This does\n\
712 not necessarily mean that all combinations of these names can be used for\n\
713 the FROM and TO command line parameters.  One coded character set can be\n\
714 listed with several different names (aliases).\n\n  "), stdout);
715
716   /* Now print the collected names.  */
717   column = 2;
718   if (isatty (fileno (stdout)))
719     {
720       twalk (printlist, do_print_human);
721
722       if (column != 0)
723         puts ("");
724     }
725   else
726     twalk (printlist, do_print);
727 }