Use GPL, not LGPL.
[kopensolaris-gnu/glibc.git] / iconv / iconv_charmap.c
1 /* Convert using charmaps and possibly iconv().
2    Copyright (C) 2001, 2005 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License version 2 as
8    published by the Free Software Foundation.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 #include <assert.h>
20 #include <errno.h>
21 #include <error.h>
22 #include <fcntl.h>
23 #include <iconv.h>
24 #include <libintl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30
31 #include "iconv_prog.h"
32
33
34 /* Prototypes for a few program-wide used functions.  */
35 extern void *xmalloc (size_t __n);
36 extern void *xcalloc (size_t __n, size_t __s);
37
38
39 struct convtable
40 {
41   int term[256 / 8];
42   union
43   {
44     struct convtable *sub;
45     struct charseq *out;
46   } val[256];
47 };
48
49
50 static inline struct convtable *
51 allocate_table (void)
52 {
53   return (struct convtable *) xcalloc (1, sizeof (struct convtable));
54 }
55
56
57 static inline int
58 is_term (struct convtable *tbl, unsigned int idx)
59 {
60   return tbl->term[idx / 8] & (1 << (idx % 8));
61 }
62
63
64 static inline void
65 clear_term (struct convtable *tbl, unsigned int idx)
66 {
67   tbl->term[idx / 8] &= ~(1 << (idx % 8));
68 }
69
70
71 static inline void
72 set_term (struct convtable *tbl, unsigned int idx)
73 {
74   tbl->term[idx / 8] |= 1 << (idx % 8);
75 }
76
77
78 /* Generate the conversion table.  */
79 static struct convtable *use_from_charmap (struct charmap_t *from_charmap,
80                                            const char *to_code);
81 static struct convtable *use_to_charmap (const char *from_code,
82                                          struct charmap_t *to_charmap);
83 static struct convtable *use_both_charmaps (struct charmap_t *from_charmap,
84                                             struct charmap_t *to_charmap);
85
86 /* Prototypes for the functions doing the actual work.  */
87 static int process_block (struct convtable *tbl, char *addr, size_t len,
88                           FILE *output);
89 static int process_fd (struct convtable *tbl, int fd, FILE *output);
90 static int process_file (struct convtable *tbl, FILE *input, FILE *output);
91
92
93 int
94 charmap_conversion (const char *from_code, struct charmap_t *from_charmap,
95                     const char *to_code, struct charmap_t *to_charmap,
96                     int argc, int remaining, char *argv[], FILE *output)
97 {
98   struct convtable *cvtbl;
99   int status = EXIT_SUCCESS;
100
101   /* We have three different cases to handle:
102
103      - both, from_charmap and to_charmap, are available.  This means we
104        can assume that the symbolic names match and use them to create
105        the mapping.
106
107      - only from_charmap is available.  In this case we can only hope that
108        the symbolic names used are of the <Uxxxx> form in which case we
109        can use a UCS4->"to_code" iconv() conversion for the second step.
110
111      - only to_charmap is available.  This is similar, only that we would
112        use iconv() for the "to_code"->UCS4 conversion.
113
114        We first create a table which maps input bytes into output bytes.
115        Once this is done we can handle all three of the cases above
116        equally.  */
117   if (from_charmap != NULL)
118     {
119       if (to_charmap == NULL)
120         cvtbl = use_from_charmap (from_charmap, to_code);
121       else
122         cvtbl = use_both_charmaps (from_charmap, to_charmap);
123     }
124   else
125     {
126       assert (to_charmap != NULL);
127       cvtbl = use_to_charmap (from_code, to_charmap);
128     }
129
130   /* If we couldn't generate a table stop now.  */
131   if (cvtbl == NULL)
132     return EXIT_FAILURE;
133
134   /* We can now start the conversion.  */
135   if (remaining == argc)
136     {
137       if (process_file (cvtbl, stdin, output) != 0)
138         status = EXIT_FAILURE;
139     }
140   else
141     do
142       {
143         struct stat st;
144         char *addr;
145         int fd;
146
147         if (verbose)
148           printf ("%s:\n", argv[remaining]);
149         if (strcmp (argv[remaining], "-") == 0)
150           fd = 0;
151         else
152           {
153             fd = open (argv[remaining], O_RDONLY);
154
155             if (fd == -1)
156               {
157                 error (0, errno, _("cannot open input file `%s'"),
158                        argv[remaining]);
159                 status = EXIT_FAILURE;
160                 continue;
161               }
162           }
163
164 #ifdef _POSIX_MAPPED_FILES
165         /* We have possibilities for reading the input file.  First try
166            to mmap() it since this will provide the fastest solution.  */
167         if (fstat (fd, &st) == 0
168             && ((addr = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE,
169                               fd, 0)) != MAP_FAILED))
170           {
171             /* Yes, we can use mmap().  The descriptor is not needed
172                anymore.  */
173             if (close (fd) != 0)
174               error (EXIT_FAILURE, errno,
175                      _("error while closing input `%s'"), argv[remaining]);
176
177             if (process_block (cvtbl, addr, st.st_size, output) < 0)
178               {
179                 /* Something went wrong.  */
180                 status = EXIT_FAILURE;
181
182                 /* We don't need the input data anymore.  */
183                 munmap ((void *) addr, st.st_size);
184
185                 /* We cannot go on with producing output since it might
186                    lead to problem because the last output might leave
187                    the output stream in an undefined state.  */
188                 break;
189               }
190
191             /* We don't need the input data anymore.  */
192             munmap ((void *) addr, st.st_size);
193           }
194         else
195 #endif  /* _POSIX_MAPPED_FILES */
196           {
197             /* Read the file in pieces.  */
198             if (process_fd (cvtbl, fd, output) != 0)
199               {
200                 /* Something went wrong.  */
201                 status = EXIT_FAILURE;
202
203                 /* We don't need the input file anymore.  */
204                 close (fd);
205
206                 /* We cannot go on with producing output since it might
207                    lead to problem because the last output might leave
208                    the output stream in an undefined state.  */
209                 break;
210               }
211
212             /* Now close the file.  */
213             close (fd);
214           }
215       }
216     while (++remaining < argc);
217
218   /* All done.  */
219   return status;
220 }
221
222
223 static void
224 add_bytes (struct convtable *tbl, struct charseq *in, struct charseq *out)
225 {
226   int n = 0;
227   unsigned int byte;
228
229   assert (in->nbytes > 0);
230
231   byte = ((unsigned char *) in->bytes)[n];
232   while (n + 1 < in->nbytes)
233     {
234       if (is_term (tbl, byte) || tbl->val[byte].sub == NULL)
235         {
236           /* Note that we simply ignore a definition for a byte sequence
237              which is also the prefix for a longer one.  */
238           clear_term (tbl, byte);
239           tbl->val[byte].sub =
240             (struct convtable *) xcalloc (1, sizeof (struct convtable));
241         }
242
243       tbl = tbl->val[byte].sub;
244
245       byte = ((unsigned char *) in->bytes)[++n];
246     }
247
248   /* Only add the new sequence if there is none yet and the byte sequence
249      is not part of an even longer one.  */
250   if (! is_term (tbl, byte) && tbl->val[byte].sub == NULL)
251     {
252       set_term (tbl, byte);
253       tbl->val[byte].out = out;
254     }
255 }
256
257
258 static struct convtable *
259 use_from_charmap (struct charmap_t *from_charmap, const char *to_code)
260 {
261   /* We iterate over all entries in the from_charmap and for those which
262      have a known UCS4 representation we use an iconv() call to determine
263      the mapping to the to_code charset.  */
264   struct convtable *rettbl;
265   iconv_t cd;
266   void *ptr = NULL;
267   const void *key;
268   size_t keylen;
269   void *data;
270
271   cd = iconv_open (to_code, "WCHAR_T");
272   if (cd == (iconv_t) -1)
273     /* We cannot do anything.  */
274     return NULL;
275
276   rettbl = allocate_table ();
277
278   while (iterate_table (&from_charmap->char_table, &ptr, &key, &keylen, &data)
279          >= 0)
280     {
281       struct charseq *in = (struct charseq *) data;
282
283       if (in->ucs4 != UNINITIALIZED_CHAR_VALUE)
284         {
285           /* There is a chance.  Try the iconv module.  */
286           wchar_t inbuf[1] = { in->ucs4 };
287           unsigned char outbuf[64];
288           char *inptr = (char *) inbuf;
289           size_t inlen = sizeof (inbuf);
290           char *outptr = (char *) outbuf;
291           size_t outlen = sizeof (outbuf);
292
293           (void) iconv (cd, &inptr, &inlen, &outptr, &outlen);
294
295           if (outptr != (char *) outbuf)
296             {
297               /* We got some output.  Good, use it.  */
298               struct charseq *newp;
299
300               outlen = sizeof (outbuf) - outlen;
301               assert ((char *) outbuf + outlen == outptr);
302
303               newp = (struct charseq *) xmalloc (sizeof (struct charseq)
304                                                  + outlen);
305               newp->name = in->name;
306               newp->ucs4 = in->ucs4;
307               newp->nbytes = outlen;
308               memcpy (newp->bytes, outbuf, outlen);
309
310               add_bytes (rettbl, in, newp);
311             }
312
313           /* Clear any possible state left behind.  */
314           (void) iconv (cd, NULL, NULL, NULL, NULL);
315         }
316     }
317
318   iconv_close (cd);
319
320   return rettbl;
321 }
322
323
324 static struct convtable *
325 use_to_charmap (const char *from_code, struct charmap_t *to_charmap)
326 {
327   /* We iterate over all entries in the to_charmap and for those which
328      have a known UCS4 representation we use an iconv() call to determine
329      the mapping to the from_code charset.  */
330   struct convtable *rettbl;
331   iconv_t cd;
332   void *ptr = NULL;
333   const void *key;
334   size_t keylen;
335   void *data;
336
337   /* Note that the conversion we use here is the reverse direction.  Without
338      exhaustive search we cannot figure out which input yields the UCS4
339      character we are looking for.  Therefore we determine it the other
340      way round.  */
341   cd = iconv_open (from_code, "WCHAR_T");
342   if (cd == (iconv_t) -1)
343     /* We cannot do anything.  */
344     return NULL;
345
346   rettbl = allocate_table ();
347
348   while (iterate_table (&to_charmap->char_table, &ptr, &key, &keylen, &data)
349          >= 0)
350     {
351       struct charseq *out = (struct charseq *) data;
352
353       if (out->ucs4 != UNINITIALIZED_CHAR_VALUE)
354         {
355           /* There is a chance.  Try the iconv module.  */
356           wchar_t inbuf[1] = { out->ucs4 };
357           unsigned char outbuf[64];
358           char *inptr = (char *) inbuf;
359           size_t inlen = sizeof (inbuf);
360           char *outptr = (char *) outbuf;
361           size_t outlen = sizeof (outbuf);
362
363           (void) iconv (cd, &inptr, &inlen, &outptr, &outlen);
364
365           if (outptr != (char *) outbuf)
366             {
367               /* We got some output.  Good, use it.  */
368               struct charseq *newp;
369
370               outlen = sizeof (outbuf) - outlen;
371               assert ((char *) outbuf + outlen == outptr);
372
373               newp = (struct charseq *) xmalloc (sizeof (struct charseq)
374                                                  + outlen);
375               newp->name = out->name;
376               newp->ucs4 = out->ucs4;
377               newp->nbytes = outlen;
378               memcpy (newp->bytes, outbuf, outlen);
379
380               add_bytes (rettbl, newp, out);
381             }
382
383           /* Clear any possible state left behind.  */
384           (void) iconv (cd, NULL, NULL, NULL, NULL);
385         }
386     }
387
388   iconv_close (cd);
389
390   return rettbl;
391 }
392
393
394 static struct convtable *
395 use_both_charmaps (struct charmap_t *from_charmap,
396                    struct charmap_t *to_charmap)
397 {
398   /* In this case we iterate over all the entries in the from_charmap,
399      determine the internal name, and find an appropriate entry in the
400      to_charmap (if it exists).  */
401   struct convtable *rettbl = allocate_table ();
402   void *ptr = NULL;
403   const void *key;
404   size_t keylen;
405   void *data;
406
407   while (iterate_table (&from_charmap->char_table, &ptr, &key, &keylen, &data)
408          >= 0)
409     {
410       struct charseq *in = (struct charseq *) data;
411       struct charseq *out = charmap_find_value (to_charmap, key, keylen);
412
413       if (out != NULL)
414         add_bytes (rettbl, in, out);
415     }
416
417   return rettbl;
418 }
419
420
421 static int
422 process_block (struct convtable *tbl, char *addr, size_t len, FILE *output)
423 {
424   size_t n = 0;
425
426   while (n < len)
427     {
428       struct convtable *cur = tbl;
429       unsigned char *curp = (unsigned char *) addr;
430       unsigned int byte = *curp;
431       int cnt;
432       struct charseq *out;
433
434       while (! is_term (cur, byte))
435         if (cur->val[byte].sub == NULL)
436           {
437             /* This is a invalid sequence.  Skip the first byte if we are
438                ignoring errors.  Otherwise punt.  */
439             if (! omit_invalid)
440               {
441                 error (0, 0, _("illegal input sequence at position %Zd"), n);
442                 return -1;
443               }
444
445             n -= curp - (unsigned char *) addr;
446
447             byte = *(curp = (unsigned char *) ++addr);
448             if (++n >= len)
449               /* All converted.  */
450               return 0;
451
452             cur = tbl;
453           }
454         else
455           {
456             cur = cur->val[byte].sub;
457
458             if (++n >= len)
459               {
460                 error (0, 0, _("\
461 incomplete character or shift sequence at end of buffer"));
462                 return -1;
463               }
464
465             byte = *++curp;
466           }
467
468       /* We found a final byte.  Write the output bytes.  */
469       out = cur->val[byte].out;
470       for (cnt = 0; cnt < out->nbytes; ++cnt)
471         fputc_unlocked (out->bytes[cnt], output);
472
473       addr = (char *) curp + 1;
474       ++n;
475     }
476
477   return 0;
478 }
479
480
481 static int
482 process_fd (struct convtable *tbl, int fd, FILE *output)
483 {
484   /* We have a problem with reading from a descriptor since we must not
485      provide the iconv() function an incomplete character or shift
486      sequence at the end of the buffer.  Since we have to deal with
487      arbitrary encodings we must read the whole text in a buffer and
488      process it in one step.  */
489   static char *inbuf = NULL;
490   static size_t maxlen = 0;
491   char *inptr = NULL;
492   size_t actlen = 0;
493
494   while (actlen < maxlen)
495     {
496       ssize_t n = read (fd, inptr, maxlen - actlen);
497
498       if (n == 0)
499         /* No more text to read.  */
500         break;
501
502       if (n == -1)
503         {
504           /* Error while reading.  */
505           error (0, errno, _("error while reading the input"));
506           return -1;
507         }
508
509       inptr += n;
510       actlen += n;
511     }
512
513   if (actlen == maxlen)
514     while (1)
515       {
516         ssize_t n;
517         char *new_inbuf;
518
519         /* Increase the buffer.  */
520         new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
521         if (new_inbuf == NULL)
522           {
523             error (0, errno, _("unable to allocate buffer for input"));
524             return -1;
525           }
526         inbuf = new_inbuf;
527         maxlen += 32768;
528         inptr = inbuf + actlen;
529
530         do
531           {
532             n = read (fd, inptr, maxlen - actlen);
533
534             if (n == 0)
535               /* No more text to read.  */
536               break;
537
538             if (n == -1)
539               {
540                 /* Error while reading.  */
541                 error (0, errno, _("error while reading the input"));
542                 return -1;
543               }
544
545             inptr += n;
546             actlen += n;
547           }
548         while (actlen < maxlen);
549
550         if (n == 0)
551           /* Break again so we leave both loops.  */
552           break;
553       }
554
555   /* Now we have all the input in the buffer.  Process it in one run.  */
556   return process_block (tbl, inbuf, actlen, output);
557 }
558
559
560 static int
561 process_file (struct convtable *tbl, FILE *input, FILE *output)
562 {
563   /* This should be safe since we use this function only for `stdin' and
564      we haven't read anything so far.  */
565   return process_fd (tbl, fileno (input), output);
566 }