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