LCOV - code coverage report
Current view: top level - gl - striconv.c (source / functions) Hit Total Coverage
Test: GNU SASL Lines: 89 143 62.2 %
Date: 2010-09-27 Functions: 3 3 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 33 68 48.5 %

           Branch data     Line data    Source code
       1                 :            : /* Charset conversion.
       2                 :            :    Copyright (C) 2001-2007, 2009-2010 Free Software Foundation, Inc.
       3                 :            :    Written by Bruno Haible and Simon Josefsson.
       4                 :            : 
       5                 :            :    This program is free software; you can redistribute it and/or modify
       6                 :            :    it under the terms of the GNU General Public License as published by
       7                 :            :    the Free Software Foundation; either version 3, or (at your option)
       8                 :            :    any later version.
       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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
      18                 :            : 
      19                 :            : #include <config.h>
      20                 :            : 
      21                 :            : /* Specification.  */
      22                 :            : #include "striconv.h"
      23                 :            : 
      24                 :            : #include <errno.h>
      25                 :            : #include <stdlib.h>
      26                 :            : #include <string.h>
      27                 :            : 
      28                 :            : #if HAVE_ICONV
      29                 :            : # include <iconv.h>
      30                 :            : /* Get MB_LEN_MAX, CHAR_BIT.  */
      31                 :            : # include <limits.h>
      32                 :            : #endif
      33                 :            : 
      34                 :            : #include "c-strcase.h"
      35                 :            : 
      36                 :            : #ifndef SIZE_MAX
      37                 :            : # define SIZE_MAX ((size_t) -1)
      38                 :            : #endif
      39                 :            : 
      40                 :            : 
      41                 :            : #if HAVE_ICONV
      42                 :            : 
      43                 :            : int
      44                 :          4 : mem_cd_iconv (const char *src, size_t srclen, iconv_t cd,
      45                 :            :               char **resultp, size_t *lengthp)
      46                 :            : {
      47                 :            : # define tmpbufsize 4096
      48                 :            :   size_t length;
      49                 :            :   char *result;
      50                 :            : 
      51                 :            :   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
      52                 :            : # if defined _LIBICONV_VERSION \
      53                 :            :      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
      54                 :            :   /* Set to the initial state.  */
      55                 :          4 :   iconv (cd, NULL, NULL, NULL, NULL);
      56                 :            : # endif
      57                 :            : 
      58                 :            :   /* Determine the length we need.  */
      59                 :            :   {
      60                 :          4 :     size_t count = 0;
      61                 :            :     /* The alignment is needed when converting e.g. to glibc's WCHAR_T or
      62                 :            :        libiconv's UCS-4-INTERNAL encoding.  */
      63                 :            :     union { unsigned int align; char buf[tmpbufsize]; } tmp;
      64                 :            : # define tmpbuf tmp.buf
      65                 :          4 :     const char *inptr = src;
      66                 :          4 :     size_t insize = srclen;
      67                 :            : 
      68         [ +  + ]:          6 :     while (insize > 0)
      69                 :            :       {
      70                 :          4 :         char *outptr = tmpbuf;
      71                 :          4 :         size_t outsize = tmpbufsize;
      72                 :          4 :         size_t res = iconv (cd,
      73                 :            :                             (ICONV_CONST char **) &inptr, &insize,
      74                 :          4 :                             &outptr, &outsize);
      75                 :            : 
      76         [ +  + ]:          4 :         if (res == (size_t)(-1))
      77                 :            :           {
      78         [ +  - ]:          2 :             if (errno == E2BIG)
      79                 :            :               ;
      80         [ +  + ]:          2 :             else if (errno == EINVAL)
      81                 :          1 :               break;
      82                 :            :             else
      83                 :          1 :               return -1;
      84                 :            :           }
      85                 :            : # if !defined _LIBICONV_VERSION && !defined __GLIBC__
      86                 :            :         /* Irix iconv() inserts a NUL byte if it cannot convert.
      87                 :            :            NetBSD iconv() inserts a question mark if it cannot convert.
      88                 :            :            Only GNU libiconv and GNU libc are known to prefer to fail rather
      89                 :            :            than doing a lossy conversion.  */
      90                 :            :         else if (res > 0)
      91                 :            :           {
      92                 :            :             errno = EILSEQ;
      93                 :            :             return -1;
      94                 :            :           }
      95                 :            : # endif
      96                 :          2 :         count += outptr - tmpbuf;
      97                 :            :       }
      98                 :            :     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
      99                 :            : # if defined _LIBICONV_VERSION \
     100                 :            :      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
     101                 :            :     {
     102                 :          3 :       char *outptr = tmpbuf;
     103                 :          3 :       size_t outsize = tmpbufsize;
     104                 :          3 :       size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
     105                 :            : 
     106         [ -  + ]:          3 :       if (res == (size_t)(-1))
     107                 :          0 :         return -1;
     108                 :          3 :       count += outptr - tmpbuf;
     109                 :            :     }
     110                 :            : # endif
     111                 :          3 :     length = count;
     112                 :            : # undef tmpbuf
     113                 :            :   }
     114                 :            : 
     115         [ +  + ]:          3 :   if (length == 0)
     116                 :            :     {
     117                 :          1 :       *lengthp = 0;
     118                 :          1 :       return 0;
     119                 :            :     }
     120 [ -  + ][ #  # ]:          2 :   if (*resultp != NULL && *lengthp >= length)
     121                 :          0 :     result = *resultp;
     122                 :            :   else
     123                 :            :     {
     124                 :          2 :       result = (char *) malloc (length);
     125         [ -  + ]:          2 :       if (result == NULL)
     126                 :            :         {
     127                 :          0 :           errno = ENOMEM;
     128                 :          0 :           return -1;
     129                 :            :         }
     130                 :            :     }
     131                 :            : 
     132                 :            :   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
     133                 :            : # if defined _LIBICONV_VERSION \
     134                 :            :      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
     135                 :            :   /* Return to the initial state.  */
     136                 :          2 :   iconv (cd, NULL, NULL, NULL, NULL);
     137                 :            : # endif
     138                 :            : 
     139                 :            :   /* Do the conversion for real.  */
     140                 :            :   {
     141                 :          2 :     const char *inptr = src;
     142                 :          2 :     size_t insize = srclen;
     143                 :          2 :     char *outptr = result;
     144                 :          2 :     size_t outsize = length;
     145                 :            : 
     146         [ +  + ]:          4 :     while (insize > 0)
     147                 :            :       {
     148                 :          2 :         size_t res = iconv (cd,
     149                 :            :                             (ICONV_CONST char **) &inptr, &insize,
     150                 :          2 :                             &outptr, &outsize);
     151                 :            : 
     152         [ -  + ]:          2 :         if (res == (size_t)(-1))
     153                 :            :           {
     154         [ #  # ]:          0 :             if (errno == EINVAL)
     155                 :          0 :               break;
     156                 :            :             else
     157                 :          0 :               goto fail;
     158                 :            :           }
     159                 :            : # if !defined _LIBICONV_VERSION && !defined __GLIBC__
     160                 :            :         /* Irix iconv() inserts a NUL byte if it cannot convert.
     161                 :            :            NetBSD iconv() inserts a question mark if it cannot convert.
     162                 :            :            Only GNU libiconv and GNU libc are known to prefer to fail rather
     163                 :            :            than doing a lossy conversion.  */
     164                 :            :         else if (res > 0)
     165                 :            :           {
     166                 :            :             errno = EILSEQ;
     167                 :            :             goto fail;
     168                 :            :           }
     169                 :            : # endif
     170                 :            :       }
     171                 :            :     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
     172                 :            : # if defined _LIBICONV_VERSION \
     173                 :            :      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
     174                 :            :     {
     175                 :          2 :       size_t res = iconv (cd, NULL, NULL, &outptr, &outsize);
     176                 :            : 
     177         [ -  + ]:          2 :       if (res == (size_t)(-1))
     178                 :          0 :         goto fail;
     179                 :            :     }
     180                 :            : # endif
     181         [ -  + ]:          2 :     if (outsize != 0)
     182                 :          0 :       abort ();
     183                 :            :   }
     184                 :            : 
     185                 :          2 :   *resultp = result;
     186                 :          2 :   *lengthp = length;
     187                 :            : 
     188                 :          2 :   return 0;
     189                 :            : 
     190                 :            :  fail:
     191                 :            :   {
     192         [ #  # ]:          0 :     if (result != *resultp)
     193                 :            :       {
     194                 :          0 :         int saved_errno = errno;
     195                 :          0 :         free (result);
     196                 :          0 :         errno = saved_errno;
     197                 :            :       }
     198                 :          4 :     return -1;
     199                 :            :   }
     200                 :            : # undef tmpbufsize
     201                 :            : }
     202                 :            : 
     203                 :            : char *
     204                 :          8 : str_cd_iconv (const char *src, iconv_t cd)
     205                 :            : {
     206                 :            :   /* For most encodings, a trailing NUL byte in the input will be converted
     207                 :            :      to a trailing NUL byte in the output.  But not for UTF-7.  So that this
     208                 :            :      function is usable for UTF-7, we have to exclude the NUL byte from the
     209                 :            :      conversion and add it by hand afterwards.  */
     210                 :            : # if !defined _LIBICONV_VERSION && !defined __GLIBC__
     211                 :            :   /* Irix iconv() inserts a NUL byte if it cannot convert.
     212                 :            :      NetBSD iconv() inserts a question mark if it cannot convert.
     213                 :            :      Only GNU libiconv and GNU libc are known to prefer to fail rather
     214                 :            :      than doing a lossy conversion.  For other iconv() implementations,
     215                 :            :      we have to look at the number of irreversible conversions returned;
     216                 :            :      but this information is lost when iconv() returns for an E2BIG reason.
     217                 :            :      Therefore we cannot use the second, faster algorithm.  */
     218                 :            : 
     219                 :            :   char *result = NULL;
     220                 :            :   size_t length = 0;
     221                 :            :   int retval = mem_cd_iconv (src, strlen (src), cd, &result, &length);
     222                 :            :   char *final_result;
     223                 :            : 
     224                 :            :   if (retval < 0)
     225                 :            :     {
     226                 :            :       if (result != NULL)
     227                 :            :         abort ();
     228                 :            :       return NULL;
     229                 :            :     }
     230                 :            : 
     231                 :            :   /* Add the terminating NUL byte.  */
     232                 :            :   final_result =
     233                 :            :     (result != NULL ? realloc (result, length + 1) : malloc (length + 1));
     234                 :            :   if (final_result == NULL)
     235                 :            :     {
     236                 :            :       free (result);
     237                 :            :       errno = ENOMEM;
     238                 :            :       return NULL;
     239                 :            :     }
     240                 :            :   final_result[length] = '\0';
     241                 :            : 
     242                 :            :   return final_result;
     243                 :            : 
     244                 :            : # else
     245                 :            :   /* This algorithm is likely faster than the one above.  But it may produce
     246                 :            :      iconv() returns for an E2BIG reason, when the output size guess is too
     247                 :            :      small.  Therefore it can only be used when we don't need the number of
     248                 :            :      irreversible conversions performed.  */
     249                 :            :   char *result;
     250                 :            :   size_t result_size;
     251                 :            :   size_t length;
     252                 :          8 :   const char *inptr = src;
     253                 :          8 :   size_t inbytes_remaining = strlen (src);
     254                 :            : 
     255                 :            :   /* Make a guess for the worst-case output size, in order to avoid a
     256                 :            :      realloc.  It's OK if the guess is wrong as long as it is not zero and
     257                 :            :      doesn't lead to an integer overflow.  */
     258                 :          8 :   result_size = inbytes_remaining;
     259                 :            :   {
     260                 :          8 :     size_t approx_sqrt_SIZE_MAX = SIZE_MAX >> (sizeof (size_t) * CHAR_BIT / 2);
     261         [ +  - ]:          8 :     if (result_size <= approx_sqrt_SIZE_MAX / MB_LEN_MAX)
     262                 :          8 :       result_size *= MB_LEN_MAX;
     263                 :            :   }
     264                 :          8 :   result_size += 1; /* for the terminating NUL */
     265                 :            : 
     266                 :          8 :   result = (char *) malloc (result_size);
     267         [ -  + ]:          8 :   if (result == NULL)
     268                 :            :     {
     269                 :          0 :       errno = ENOMEM;
     270                 :          0 :       return NULL;
     271                 :            :     }
     272                 :            : 
     273                 :            :   /* Avoid glibc-2.1 bug and Solaris 2.7-2.9 bug.  */
     274                 :            : # if defined _LIBICONV_VERSION \
     275                 :            :      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
     276                 :            :   /* Set to the initial state.  */
     277                 :          8 :   iconv (cd, NULL, NULL, NULL, NULL);
     278                 :            : # endif
     279                 :            : 
     280                 :            :   /* Do the conversion.  */
     281                 :            :   {
     282                 :          8 :     char *outptr = result;
     283                 :          8 :     size_t outbytes_remaining = result_size - 1;
     284                 :            : 
     285                 :            :     for (;;)
     286                 :            :       {
     287                 :            :         /* Here inptr + inbytes_remaining = src + strlen (src),
     288                 :            :                 outptr + outbytes_remaining = result + result_size - 1.  */
     289                 :          8 :         size_t res = iconv (cd,
     290                 :            :                             (ICONV_CONST char **) &inptr, &inbytes_remaining,
     291                 :          8 :                             &outptr, &outbytes_remaining);
     292                 :            : 
     293         [ +  + ]:          8 :         if (res == (size_t)(-1))
     294                 :            :           {
     295         [ +  + ]:          4 :             if (errno == EINVAL)
     296                 :          2 :               break;
     297         [ -  + ]:          2 :             else if (errno == E2BIG)
     298                 :            :               {
     299                 :          0 :                 size_t used = outptr - result;
     300                 :          0 :                 size_t newsize = result_size * 2;
     301                 :            :                 char *newresult;
     302                 :            : 
     303         [ #  # ]:          0 :                 if (!(newsize > result_size))
     304                 :            :                   {
     305                 :          0 :                     errno = ENOMEM;
     306                 :          0 :                     goto failed;
     307                 :            :                   }
     308                 :          0 :                 newresult = (char *) realloc (result, newsize);
     309         [ #  # ]:          0 :                 if (newresult == NULL)
     310                 :            :                   {
     311                 :          0 :                     errno = ENOMEM;
     312                 :          0 :                     goto failed;
     313                 :            :                   }
     314                 :          0 :                 result = newresult;
     315                 :          0 :                 result_size = newsize;
     316                 :          0 :                 outptr = result + used;
     317                 :          0 :                 outbytes_remaining = result_size - 1 - used;
     318                 :            :               }
     319                 :            :             else
     320                 :          2 :               goto failed;
     321                 :            :           }
     322                 :            :         else
     323                 :          4 :           break;
     324                 :          0 :       }
     325                 :            :     /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
     326                 :            : # if defined _LIBICONV_VERSION \
     327                 :            :      || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)
     328                 :            :     for (;;)
     329                 :            :       {
     330                 :            :         /* Here outptr + outbytes_remaining = result + result_size - 1.  */
     331                 :          6 :         size_t res = iconv (cd, NULL, NULL, &outptr, &outbytes_remaining);
     332                 :            : 
     333         [ -  + ]:          6 :         if (res == (size_t)(-1))
     334                 :            :           {
     335         [ #  # ]:          0 :             if (errno == E2BIG)
     336                 :            :               {
     337                 :          0 :                 size_t used = outptr - result;
     338                 :          0 :                 size_t newsize = result_size * 2;
     339                 :            :                 char *newresult;
     340                 :            : 
     341         [ #  # ]:          0 :                 if (!(newsize > result_size))
     342                 :            :                   {
     343                 :          0 :                     errno = ENOMEM;
     344                 :          0 :                     goto failed;
     345                 :            :                   }
     346                 :          0 :                 newresult = (char *) realloc (result, newsize);
     347         [ #  # ]:          0 :                 if (newresult == NULL)
     348                 :            :                   {
     349                 :          0 :                     errno = ENOMEM;
     350                 :          0 :                     goto failed;
     351                 :            :                   }
     352                 :          0 :                 result = newresult;
     353                 :          0 :                 result_size = newsize;
     354                 :          0 :                 outptr = result + used;
     355                 :          0 :                 outbytes_remaining = result_size - 1 - used;
     356                 :            :               }
     357                 :            :             else
     358                 :          0 :               goto failed;
     359                 :            :           }
     360                 :            :         else
     361                 :            :           break;
     362                 :          0 :       }
     363                 :            : # endif
     364                 :            : 
     365                 :            :     /* Add the terminating NUL byte.  */
     366                 :          6 :     *outptr++ = '\0';
     367                 :            : 
     368                 :          6 :     length = outptr - result;
     369                 :            :   }
     370                 :            : 
     371                 :            :   /* Give away unused memory.  */
     372         [ +  - ]:          6 :   if (length < result_size)
     373                 :            :     {
     374                 :          6 :       char *smaller_result = (char *) realloc (result, length);
     375                 :            : 
     376         [ +  - ]:          6 :       if (smaller_result != NULL)
     377                 :          6 :         result = smaller_result;
     378                 :            :     }
     379                 :            : 
     380                 :          6 :   return result;
     381                 :            : 
     382                 :            :  failed:
     383                 :            :   {
     384                 :          2 :     int saved_errno = errno;
     385                 :          2 :     free (result);
     386                 :          2 :     errno = saved_errno;
     387                 :          8 :     return NULL;
     388                 :            :   }
     389                 :            : 
     390                 :            : # endif
     391                 :            : }
     392                 :            : 
     393                 :            : #endif
     394                 :            : 
     395                 :            : char *
     396                 :          4 : str_iconv (const char *src, const char *from_codeset, const char *to_codeset)
     397                 :            : {
     398 [ +  - ][ -  + ]:          4 :   if (*src == '\0' || c_strcasecmp (from_codeset, to_codeset) == 0)
     399                 :            :     {
     400                 :          0 :       char *result = strdup (src);
     401                 :            : 
     402         [ #  # ]:          0 :       if (result == NULL)
     403                 :          0 :         errno = ENOMEM;
     404                 :          0 :       return result;
     405                 :            :     }
     406                 :            :   else
     407                 :            :     {
     408                 :            : #if HAVE_ICONV
     409                 :            :       iconv_t cd;
     410                 :            :       char *result;
     411                 :            : 
     412                 :            :       /* Avoid glibc-2.1 bug with EUC-KR.  */
     413                 :            : # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
     414                 :            :       if (c_strcasecmp (from_codeset, "EUC-KR") == 0
     415                 :            :           || c_strcasecmp (to_codeset, "EUC-KR") == 0)
     416                 :            :         {
     417                 :            :           errno = EINVAL;
     418                 :            :           return NULL;
     419                 :            :         }
     420                 :            : # endif
     421                 :          4 :       cd = iconv_open (to_codeset, from_codeset);
     422         [ -  + ]:          4 :       if (cd == (iconv_t) -1)
     423                 :          0 :         return NULL;
     424                 :            : 
     425                 :          4 :       result = str_cd_iconv (src, cd);
     426                 :            : 
     427         [ +  + ]:          4 :       if (result == NULL)
     428                 :            :         {
     429                 :            :           /* Close cd, but preserve the errno from str_cd_iconv.  */
     430                 :          1 :           int saved_errno = errno;
     431                 :          1 :           iconv_close (cd);
     432                 :          1 :           errno = saved_errno;
     433                 :            :         }
     434                 :            :       else
     435                 :            :         {
     436         [ -  + ]:          3 :           if (iconv_close (cd) < 0)
     437                 :            :             {
     438                 :            :               /* Return NULL, but free the allocated memory, and while doing
     439                 :            :                  that, preserve the errno from iconv_close.  */
     440                 :          0 :               int saved_errno = errno;
     441                 :          0 :               free (result);
     442                 :          0 :               errno = saved_errno;
     443                 :          0 :               return NULL;
     444                 :            :             }
     445                 :            :         }
     446                 :          4 :       return result;
     447                 :            : #else
     448                 :            :       /* This is a different error code than if iconv_open existed but didn't
     449                 :            :          support from_codeset and to_codeset, so that the caller can emit
     450                 :            :          an error message such as
     451                 :            :            "iconv() is not supported. Installing GNU libiconv and
     452                 :            :             then reinstalling this package would fix this."  */
     453                 :            :       errno = ENOSYS;
     454                 :            :       return NULL;
     455                 :            : #endif
     456                 :            :     }
     457                 :            : }

Generated by: LCOV version 1.8