iconvme.c

Go to the documentation of this file.
00001 /* Recode strings between character sets, using iconv.
00002    Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
00003 
00004    This program is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Lesser General Public License as
00006    published by the Free Software Foundation; either version 2.1, or (at
00007    your option) any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012    GNU Lesser General Public License for more details.
00013 
00014    You should have received a copy of the GNU Lesser General Public License along
00015    with this program; if not, write to the Free Software Foundation,
00016    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
00017 
00018 #ifdef HAVE_CONFIG_H
00019 # include <config.h>
00020 #endif
00021 
00022 /* Get prototype. */
00023 #include "iconvme.h"
00024 
00025 /* Get malloc. */
00026 #include <stdlib.h>
00027 
00028 /* Get strcmp. */
00029 #include <string.h>
00030 
00031 /* Get errno. */
00032 #include <errno.h>
00033 
00034 #ifdef _LIBC
00035 # define HAVE_ICONV 1
00036 #else
00037 /* Get strdup. */
00038 # include "strdup.h"
00039 #endif
00040 
00041 #if HAVE_ICONV
00042 /* Get iconv etc. */
00043 # include <iconv.h>
00044 /* Get MB_LEN_MAX, CHAR_BIT.  */
00045 # include <limits.h>
00046 #endif
00047 
00048 #ifndef SIZE_MAX
00049 # define SIZE_MAX ((size_t) -1)
00050 #endif
00051 
00052 /* Convert a zero-terminated string STR from the FROM_CODSET code set
00053    to the TO_CODESET code set.  The returned string is allocated using
00054    malloc, and must be dellocated by the caller using free.  On
00055    failure, NULL is returned and errno holds the error reason.  Note
00056    that if TO_CODESET uses \0 for anything but to terminate the
00057    string, the caller of this function may have difficulties finding
00058    out the length of the output string.  */
00059 char *
00060 iconv_string (const char *str, const char *from_codeset,
00061               const char *to_codeset)
00062 {
00063   char *dest = NULL;
00064 #if HAVE_ICONV
00065   iconv_t cd;
00066   char *outp;
00067   char *p = (char *) str;
00068   size_t inbytes_remaining = strlen (p);
00069   /* Guess the maximum length the output string can have.  */
00070   size_t outbuf_size = inbytes_remaining + 1;
00071   size_t outbytes_remaining;
00072   size_t err;
00073   int have_error = 0;
00074 
00075   /* Use a worst-case output size guess, so long as that wouldn't be
00076      too large for comfort.  It's OK if the guess is wrong so long as
00077      it's nonzero.  */
00078   size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
00079   if (outbuf_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
00080     outbuf_size *= MB_LEN_MAX;
00081   outbytes_remaining = outbuf_size - 1;
00082 #endif
00083 
00084   if (strcmp (to_codeset, from_codeset) == 0)
00085     return strdup (str);
00086 
00087 #if HAVE_ICONV
00088   cd = iconv_open (to_codeset, from_codeset);
00089   if (cd == (iconv_t) -1)
00090     return NULL;
00091 
00092   outp = dest = (char *) malloc (outbuf_size);
00093   if (dest == NULL)
00094     goto out;
00095 
00096 again:
00097   err = iconv (cd, &p, &inbytes_remaining, &outp, &outbytes_remaining);
00098 
00099   if (err == (size_t) - 1)
00100     {
00101       switch (errno)
00102         {
00103         case EINVAL:
00104           /* Incomplete text, do not report an error */
00105           break;
00106 
00107         case E2BIG:
00108           {
00109             size_t used = outp - dest;
00110             size_t newsize = outbuf_size * 2;
00111             char *newdest;
00112 
00113             if (newsize <= outbuf_size)
00114               {
00115                 errno = ENOMEM;
00116                 have_error = 1;
00117                 goto out;
00118               }
00119             newdest = (char *) realloc (dest, newsize);
00120             if (newdest == NULL)
00121               {
00122                 have_error = 1;
00123                 goto out;
00124               }
00125             dest = newdest;
00126             outbuf_size = newsize;
00127 
00128             outp = dest + used;
00129             outbytes_remaining = outbuf_size - used - 1;        /* -1 for NUL */
00130 
00131             goto again;
00132           }
00133           break;
00134 
00135         case EILSEQ:
00136           have_error = 1;
00137           break;
00138 
00139         default:
00140           have_error = 1;
00141           break;
00142         }
00143     }
00144 
00145   *outp = '\0';
00146 
00147 out:
00148   {
00149     int save_errno = errno;
00150 
00151     if (iconv_close (cd) < 0 && !have_error)
00152       {
00153         /* If we didn't have a real error before, make sure we restore
00154            the iconv_close error below. */
00155         save_errno = errno;
00156         have_error = 1;
00157       }
00158 
00159     if (have_error && dest)
00160       {
00161         free (dest);
00162         dest = NULL;
00163         errno = save_errno;
00164       }
00165   }
00166 #else
00167   errno = ENOSYS;
00168 #endif
00169 
00170   return dest;
00171 }

Generated on Wed Sep 13 10:20:31 2006 for libidn by  doxygen 1.4.7