Back to index

glibc  2.9
gconv_open.c
Go to the documentation of this file.
00001 /* Find matching transformation algorithms and initialize steps.
00002    Copyright (C) 1997,1998,1999,2000,2001,2004,2005,2007
00003        Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
00006 
00007    The GNU C Library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public
00009    License as published by the Free Software Foundation; either
00010    version 2.1 of the License, or (at your option) any later version.
00011 
00012    The GNU C Library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License along with the GNU C Library; if not, write to the Free
00019    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00020    02111-1307 USA.  */
00021 
00022 #include <errno.h>
00023 #include <locale.h>
00024 #include "../locale/localeinfo.h"
00025 #include <stdlib.h>
00026 #include <string.h>
00027 
00028 #include <gconv_int.h>
00029 
00030 
00031 int
00032 internal_function
00033 __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
00034              int flags)
00035 {
00036   struct __gconv_step *steps;
00037   size_t nsteps;
00038   __gconv_t result = NULL;
00039   size_t cnt = 0;
00040   int res;
00041   int conv_flags = 0;
00042   const char *errhand;
00043   const char *ignore;
00044   struct trans_struct *trans = NULL;
00045 
00046   /* Find out whether any error handling method is specified.  */
00047   errhand = strchr (toset, '/');
00048   if (errhand != NULL)
00049     errhand = strchr (errhand + 1, '/');
00050   if (__builtin_expect (errhand != NULL, 1))
00051     {
00052       if (*++errhand == '\0')
00053        errhand = NULL;
00054       else
00055        {
00056          /* Make copy without the error handling description.  */
00057          char *newtoset = (char *) alloca (errhand - toset + 1);
00058          char *tok;
00059          char *ptr = NULL /* Work around a bogus warning */;
00060 
00061          newtoset[errhand - toset] = '\0';
00062          toset = memcpy (newtoset, toset, errhand - toset);
00063 
00064          /* Find the appropriate transliteration handlers.  */
00065          tok = strdupa (errhand);
00066 
00067          tok = __strtok_r (tok, ",", &ptr);
00068          while (tok != NULL)
00069            {
00070              if (__strcasecmp_l (tok, "TRANSLIT", _nl_C_locobj_ptr) == 0)
00071               {
00072                 /* It's the builtin transliteration handling.  We only
00073                    support it for working on the internal encoding.  */
00074                 static const char *const internal_trans_names[1]
00075                   = { "INTERNAL" };
00076                 struct trans_struct *lastp = NULL;
00077                 struct trans_struct *runp;
00078 
00079                 for (runp = trans; runp != NULL; runp = runp->next)
00080                   if (runp->trans_fct == __gconv_transliterate)
00081                     break;
00082                   else
00083                     lastp = runp;
00084 
00085                 if (runp == NULL)
00086                   {
00087                     struct trans_struct *newp;
00088 
00089                     newp = (struct trans_struct *) alloca (sizeof (*newp));
00090                     memset (newp, '\0', sizeof (*newp));
00091 
00092                     /* We leave the `name' field zero to signal that
00093                       this is an internal transliteration step.  */
00094                     newp->csnames = (const char **) internal_trans_names;
00095                     newp->ncsnames = 1;
00096                     newp->trans_fct = __gconv_transliterate;
00097 
00098                     if (lastp == NULL)
00099                      trans = newp;
00100                     else
00101                      lastp->next = newp;
00102                   }
00103               }
00104              else if (__strcasecmp_l (tok, "IGNORE", _nl_C_locobj_ptr) == 0)
00105               /* Set the flag to ignore all errors.  */
00106               conv_flags |= __GCONV_IGNORE_ERRORS;
00107              else
00108               {
00109                 /* `tok' is possibly a module name.  We'll see later
00110                    whether we can find it.  But first see that we do
00111                    not already a module of this name.  */
00112                 struct trans_struct *lastp = NULL;
00113                 struct trans_struct *runp;
00114 
00115                 for (runp = trans; runp != NULL; runp = runp->next)
00116                   if (runp->name != NULL
00117                      && __strcasecmp_l (tok, runp->name,
00118                                       _nl_C_locobj_ptr) == 0)
00119                     break;
00120                   else
00121                     lastp = runp;
00122 
00123                 if (runp == NULL)
00124                   {
00125                     struct trans_struct *newp;
00126 
00127                     newp = (struct trans_struct *) alloca (sizeof (*newp));
00128                     memset (newp, '\0', sizeof (*newp));
00129                     newp->name = tok;
00130 
00131                     if (lastp == NULL)
00132                      trans = newp;
00133                     else
00134                      lastp->next = newp;
00135                   }
00136               }
00137 
00138              tok = __strtok_r (NULL, ",", &ptr);
00139            }
00140        }
00141     }
00142 
00143   /* For the source character set we ignore the error handler specification.
00144      XXX Is this really always the best?  */
00145   ignore = strchr (fromset, '/');
00146   if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL
00147       && *++ignore != '\0')
00148     {
00149       char *newfromset = (char *) alloca (ignore - fromset + 1);
00150 
00151       newfromset[ignore - fromset] = '\0';
00152       fromset = memcpy (newfromset, fromset, ignore - fromset);
00153     }
00154 
00155   /* If the string is empty define this to mean the charset of the
00156      currently selected locale.  */
00157   if (strcmp (toset, "//") == 0)
00158     {
00159       const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
00160       size_t len = strlen (codeset);
00161       char *dest;
00162       toset = dest = (char *) alloca (len + 3);
00163       memcpy (__mempcpy (dest, codeset, len), "//", 3);
00164     }
00165   if (strcmp (fromset, "//") == 0)
00166     {
00167       const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
00168       size_t len = strlen (codeset);
00169       char *dest;
00170       fromset = dest = (char *) alloca (len + 3);
00171       memcpy (__mempcpy (dest, codeset, len), "//", 3);
00172     }
00173 
00174   res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags);
00175   if (res == __GCONV_OK)
00176     {
00177       /* Find the modules.  */
00178       struct trans_struct *lastp = NULL;
00179       struct trans_struct *runp;
00180 
00181       for (runp = trans; runp != NULL; runp = runp->next)
00182        {
00183          if (runp->name == NULL
00184              || __builtin_expect (__gconv_translit_find (runp), 0) == 0)
00185            lastp = runp;
00186          else
00187            {
00188              /* This means we haven't found the module.  Remove it.  */
00189              if (lastp == NULL)
00190               trans  = runp->next;
00191              else
00192               lastp->next  = runp->next;
00193            }
00194        }
00195 
00196       /* Allocate room for handle.  */
00197       result = (__gconv_t) malloc (sizeof (struct __gconv_info)
00198                                + (nsteps
00199                                   * sizeof (struct __gconv_step_data)));
00200       if (result == NULL)
00201        res = __GCONV_NOMEM;
00202       else
00203        {
00204          size_t n;
00205 
00206          /* Remember the list of steps.  */
00207          result->__steps = steps;
00208          result->__nsteps = nsteps;
00209 
00210          /* Clear the array for the step data.  */
00211          memset (result->__data, '\0',
00212                 nsteps * sizeof (struct __gconv_step_data));
00213 
00214          /* Call all initialization functions for the transformation
00215             step implementations.  */
00216          for (cnt = 0; cnt < nsteps; ++cnt)
00217            {
00218              size_t size;
00219 
00220              /* Would have to be done if we would not clear the whole
00221                  array above.  */
00222 #if 0
00223              /* Reset the counter.  */
00224              result->__data[cnt].__invocation_counter = 0;
00225 
00226              /* It's a regular use.  */
00227              result->__data[cnt].__internal_use = 0;
00228 #endif
00229 
00230              /* We use the `mbstate_t' member in DATA.  */
00231              result->__data[cnt].__statep = &result->__data[cnt].__state;
00232 
00233              /* Now see whether we can use any of the transliteration
00234                modules for this step.  */
00235              for (runp = trans; runp != NULL; runp = runp->next)
00236               for (n = 0; n < runp->ncsnames; ++n)
00237                 if (__strcasecmp_l (steps[cnt].__from_name,
00238                                   runp->csnames[n], _nl_C_locobj_ptr) == 0)
00239                   {
00240                     void *data = NULL;
00241 
00242                     /* Match!  Now try the initializer.  */
00243                     if (runp->trans_init_fct == NULL
00244                        || (runp->trans_init_fct (&data,
00245                                               steps[cnt].__to_name)
00246                            == __GCONV_OK))
00247                      {
00248                        /* Append at the end of the list.  */
00249                        struct __gconv_trans_data *newp;
00250                        struct __gconv_trans_data **lastp;
00251 
00252                        newp = (struct __gconv_trans_data *)
00253                          malloc (sizeof (struct __gconv_trans_data));
00254                        if (newp == NULL)
00255                          {
00256                            res = __GCONV_NOMEM;
00257                            goto bail;
00258                          }
00259 
00260                        newp->__trans_fct = runp->trans_fct;
00261                        newp->__trans_context_fct = runp->trans_context_fct;
00262                        newp->__trans_end_fct = runp->trans_end_fct;
00263                        newp->__data = data;
00264                        newp->__next = NULL;
00265 
00266                        lastp = &result->__data[cnt].__trans;
00267                        while (*lastp != NULL)
00268                          lastp = &(*lastp)->__next;
00269 
00270                        *lastp = newp;
00271                      }
00272                     break;
00273                   }
00274 
00275              /* If this is the last step we must not allocate an
00276                output buffer.  */
00277              if (cnt < nsteps - 1)
00278               {
00279                 result->__data[cnt].__flags = conv_flags;
00280 
00281                 /* Allocate the buffer.  */
00282                 size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to);
00283 
00284                 result->__data[cnt].__outbuf = malloc (size);
00285                 if (result->__data[cnt].__outbuf == NULL)
00286                   {
00287                     res = __GCONV_NOMEM;
00288                     goto bail;
00289                   }
00290 
00291                 result->__data[cnt].__outbufend =
00292                   result->__data[cnt].__outbuf + size;
00293               }
00294              else
00295               {
00296                 /* Handle the last entry.  */
00297                 result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST;
00298 
00299                 break;
00300               }
00301            }
00302        }
00303 
00304       if (res != __GCONV_OK)
00305        {
00306          /* Something went wrong.  Free all the resources.  */
00307          int serrno;
00308        bail:
00309          serrno = errno;
00310 
00311          if (result != NULL)
00312            {
00313              while (cnt-- > 0)
00314               {
00315                 struct __gconv_trans_data *transp;
00316 
00317                 transp = result->__data[cnt].__trans;
00318                 while (transp != NULL)
00319                   {
00320                     struct __gconv_trans_data *curp = transp;
00321                     transp = transp->__next;
00322 
00323                     if (__builtin_expect (curp->__trans_end_fct != NULL, 0))
00324                      curp->__trans_end_fct (curp->__data);
00325 
00326                     free (curp);
00327                   }
00328 
00329                 free (result->__data[cnt].__outbuf);
00330               }
00331 
00332              free (result);
00333              result = NULL;
00334            }
00335 
00336          __gconv_close_transform (steps, nsteps);
00337 
00338          __set_errno (serrno);
00339        }
00340     }
00341 
00342   *handle = result;
00343   return res;
00344 }