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