(__libc_memalign): Likewise. Handle malloc (0).
[kopensolaris-gnu/glibc.git] / iconv / iconv_charmap.c
1 /* Convert using charmaps and possibly iconv().
2    Copyright (C) 2001, 2005, 2006 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               union
369               {
370                 struct charseq seq;
371                 struct
372                 {
373                   const char *name;
374                   uint32_t ucs4;
375                   int nbytes;
376                   unsigned char bytes[outlen];
377                 } mem;
378               } new;
379
380               outlen = sizeof (outbuf) - outlen;
381               assert ((char *) outbuf + outlen == outptr);
382
383               new.mem.name = out->name;
384               new.mem.ucs4 = out->ucs4;
385               new.mem.nbytes = outlen;
386               memcpy (new.mem.bytes, outbuf, outlen);
387
388               add_bytes (rettbl, &new.seq, out);
389             }
390
391           /* Clear any possible state left behind.  */
392           (void) iconv (cd, NULL, NULL, NULL, NULL);
393         }
394     }
395
396   iconv_close (cd);
397
398   return rettbl;
399 }
400
401
402 static struct convtable *
403 use_both_charmaps (struct charmap_t *from_charmap,
404                    struct charmap_t *to_charmap)
405 {
406   /* In this case we iterate over all the entries in the from_charmap,
407      determine the internal name, and find an appropriate entry in the
408      to_charmap (if it exists).  */
409   struct convtable *rettbl = allocate_table ();
410   void *ptr = NULL;
411   const void *key;
412   size_t keylen;
413   void *data;
414
415   while (iterate_table (&from_charmap->char_table, &ptr, &key, &keylen, &data)
416          >= 0)
417     {
418       struct charseq *in = (struct charseq *) data;
419       struct charseq *out = charmap_find_value (to_charmap, key, keylen);
420
421       if (out != NULL)
422         add_bytes (rettbl, in, out);
423     }
424
425   return rettbl;
426 }
427
428
429 static int
430 process_block (struct convtable *tbl, char *addr, size_t len, FILE *output)
431 {
432   size_t n = 0;
433
434   while (n < len)
435     {
436       struct convtable *cur = tbl;
437       unsigned char *curp = (unsigned char *) addr;
438       unsigned int byte = *curp;
439       int cnt;
440       struct charseq *out;
441
442       while (! is_term (cur, byte))
443         if (cur->val[byte].sub == NULL)
444           {
445             /* This is a invalid sequence.  Skip the first byte if we are
446                ignoring errors.  Otherwise punt.  */
447             if (! omit_invalid)
448               {
449                 error (0, 0, _("illegal input sequence at position %Zd"), n);
450                 return -1;
451               }
452
453             n -= curp - (unsigned char *) addr;
454
455             byte = *(curp = (unsigned char *) ++addr);
456             if (++n >= len)
457               /* All converted.  */
458               return 0;
459
460             cur = tbl;
461           }
462         else
463           {
464             cur = cur->val[byte].sub;
465
466             if (++n >= len)
467               {
468                 error (0, 0, _("\
469 incomplete character or shift sequence at end of buffer"));
470                 return -1;
471               }
472
473             byte = *++curp;
474           }
475
476       /* We found a final byte.  Write the output bytes.  */
477       out = cur->val[byte].out;
478       for (cnt = 0; cnt < out->nbytes; ++cnt)
479         fputc_unlocked (out->bytes[cnt], output);
480
481       addr = (char *) curp + 1;
482       ++n;
483     }
484
485   return 0;
486 }
487
488
489 static int
490 process_fd (struct convtable *tbl, int fd, FILE *output)
491 {
492   /* We have a problem with reading from a descriptor since we must not
493      provide the iconv() function an incomplete character or shift
494      sequence at the end of the buffer.  Since we have to deal with
495      arbitrary encodings we must read the whole text in a buffer and
496      process it in one step.  */
497   static char *inbuf = NULL;
498   static size_t maxlen = 0;
499   char *inptr = inbuf;
500   size_t actlen = 0;
501
502   while (actlen < maxlen)
503     {
504       ssize_t n = read (fd, inptr, maxlen - actlen);
505
506       if (n == 0)
507         /* No more text to read.  */
508         break;
509
510       if (n == -1)
511         {
512           /* Error while reading.  */
513           error (0, errno, _("error while reading the input"));
514           return -1;
515         }
516
517       inptr += n;
518       actlen += n;
519     }
520
521   if (actlen == maxlen)
522     while (1)
523       {
524         ssize_t n;
525         char *new_inbuf;
526
527         /* Increase the buffer.  */
528         new_inbuf = (char *) realloc (inbuf, maxlen + 32768);
529         if (new_inbuf == NULL)
530           {
531             error (0, errno, _("unable to allocate buffer for input"));
532             return -1;
533           }
534         inbuf = new_inbuf;
535         maxlen += 32768;
536         inptr = inbuf + actlen;
537
538         do
539           {
540             n = read (fd, inptr, maxlen - actlen);
541
542             if (n == 0)
543               /* No more text to read.  */
544               break;
545
546             if (n == -1)
547               {
548                 /* Error while reading.  */
549                 error (0, errno, _("error while reading the input"));
550                 return -1;
551               }
552
553             inptr += n;
554             actlen += n;
555           }
556         while (actlen < maxlen);
557
558         if (n == 0)
559           /* Break again so we leave both loops.  */
560           break;
561       }
562
563   /* Now we have all the input in the buffer.  Process it in one run.  */
564   return process_block (tbl, inbuf, actlen, output);
565 }
566
567
568 static int
569 process_file (struct convtable *tbl, FILE *input, FILE *output)
570 {
571   /* This should be safe since we use this function only for `stdin' and
572      we haven't read anything so far.  */
573   return process_fd (tbl, fileno (input), output);
574 }