Back to index

glibc  2.9
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 }