| /* Find matching transformation algorithms and initialize steps. |
| Copyright (C) 1997-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <errno.h> |
| #include <locale.h> |
| #include "../locale/localeinfo.h" |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <gconv_int.h> |
| |
| |
| int |
| internal_function |
| __gconv_open (const char *toset, const char *fromset, __gconv_t *handle, |
| int flags) |
| { |
| struct __gconv_step *steps; |
| size_t nsteps; |
| __gconv_t result = NULL; |
| size_t cnt = 0; |
| int res; |
| int conv_flags = 0; |
| const char *errhand; |
| const char *ignore; |
| struct trans_struct *trans = NULL; |
| |
| /* Find out whether any error handling method is specified. */ |
| errhand = strchr (toset, '/'); |
| if (errhand != NULL) |
| errhand = strchr (errhand + 1, '/'); |
| if (__builtin_expect (errhand != NULL, 1)) |
| { |
| if (*++errhand == '\0') |
| errhand = NULL; |
| else |
| { |
| /* Make copy without the error handling description. */ |
| char *newtoset = (char *) alloca (errhand - toset + 1); |
| char *tok; |
| char *ptr = NULL /* Work around a bogus warning */; |
| |
| newtoset[errhand - toset] = '\0'; |
| toset = memcpy (newtoset, toset, errhand - toset); |
| |
| /* Find the appropriate transliteration handlers. */ |
| tok = strdupa (errhand); |
| |
| tok = __strtok_r (tok, ",", &ptr); |
| while (tok != NULL) |
| { |
| if (__strcasecmp_l (tok, "TRANSLIT", _nl_C_locobj_ptr) == 0) |
| { |
| /* It's the builtin transliteration handling. We only |
| support it for working on the internal encoding. */ |
| static const char *const internal_trans_names[1] |
| = { "INTERNAL" }; |
| struct trans_struct *lastp = NULL; |
| struct trans_struct *runp; |
| |
| for (runp = trans; runp != NULL; runp = runp->next) |
| if (runp->trans_fct == __gconv_transliterate) |
| break; |
| else |
| lastp = runp; |
| |
| if (runp == NULL) |
| { |
| struct trans_struct *newp; |
| |
| newp = (struct trans_struct *) alloca (sizeof (*newp)); |
| memset (newp, '\0', sizeof (*newp)); |
| |
| /* We leave the `name' field zero to signal that |
| this is an internal transliteration step. */ |
| newp->csnames = (const char **) internal_trans_names; |
| newp->ncsnames = 1; |
| newp->trans_fct = __gconv_transliterate; |
| |
| if (lastp == NULL) |
| trans = newp; |
| else |
| lastp->next = newp; |
| } |
| } |
| else if (__strcasecmp_l (tok, "IGNORE", _nl_C_locobj_ptr) == 0) |
| /* Set the flag to ignore all errors. */ |
| conv_flags |= __GCONV_IGNORE_ERRORS; |
| else |
| { |
| /* `tok' is possibly a module name. We'll see later |
| whether we can find it. But first see that we do |
| not already a module of this name. */ |
| struct trans_struct *lastp = NULL; |
| struct trans_struct *runp; |
| |
| for (runp = trans; runp != NULL; runp = runp->next) |
| if (runp->name != NULL |
| && __strcasecmp_l (tok, runp->name, |
| _nl_C_locobj_ptr) == 0) |
| break; |
| else |
| lastp = runp; |
| |
| if (runp == NULL) |
| { |
| struct trans_struct *newp; |
| |
| newp = (struct trans_struct *) alloca (sizeof (*newp)); |
| memset (newp, '\0', sizeof (*newp)); |
| newp->name = tok; |
| |
| if (lastp == NULL) |
| trans = newp; |
| else |
| lastp->next = newp; |
| } |
| } |
| |
| tok = __strtok_r (NULL, ",", &ptr); |
| } |
| } |
| } |
| |
| /* For the source character set we ignore the error handler specification. |
| XXX Is this really always the best? */ |
| ignore = strchr (fromset, '/'); |
| if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL |
| && *++ignore != '\0') |
| { |
| char *newfromset = (char *) alloca (ignore - fromset + 1); |
| |
| newfromset[ignore - fromset] = '\0'; |
| fromset = memcpy (newfromset, fromset, ignore - fromset); |
| } |
| |
| /* If the string is empty define this to mean the charset of the |
| currently selected locale. */ |
| if (strcmp (toset, "//") == 0) |
| { |
| const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET); |
| size_t len = strlen (codeset); |
| char *dest; |
| toset = dest = (char *) alloca (len + 3); |
| memcpy (__mempcpy (dest, codeset, len), "//", 3); |
| } |
| if (strcmp (fromset, "//") == 0) |
| { |
| const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET); |
| size_t len = strlen (codeset); |
| char *dest; |
| fromset = dest = (char *) alloca (len + 3); |
| memcpy (__mempcpy (dest, codeset, len), "//", 3); |
| } |
| |
| res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags); |
| if (res == __GCONV_OK) |
| { |
| /* Find the modules. */ |
| struct trans_struct *lastp = NULL; |
| struct trans_struct *runp; |
| |
| for (runp = trans; runp != NULL; runp = runp->next) |
| { |
| if (runp->name == NULL |
| || __builtin_expect (__gconv_translit_find (runp), 0) == 0) |
| lastp = runp; |
| else |
| { |
| /* This means we haven't found the module. Remove it. */ |
| if (lastp == NULL) |
| trans = runp->next; |
| else |
| lastp->next = runp->next; |
| } |
| } |
| |
| /* Allocate room for handle. */ |
| result = (__gconv_t) malloc (sizeof (struct __gconv_info) |
| + (nsteps |
| * sizeof (struct __gconv_step_data))); |
| if (result == NULL) |
| res = __GCONV_NOMEM; |
| else |
| { |
| size_t n; |
| |
| /* Remember the list of steps. */ |
| result->__steps = steps; |
| result->__nsteps = nsteps; |
| |
| /* Clear the array for the step data. */ |
| memset (result->__data, '\0', |
| nsteps * sizeof (struct __gconv_step_data)); |
| |
| /* Call all initialization functions for the transformation |
| step implementations. */ |
| for (cnt = 0; cnt < nsteps; ++cnt) |
| { |
| size_t size; |
| |
| /* Would have to be done if we would not clear the whole |
| array above. */ |
| #if 0 |
| /* Reset the counter. */ |
| result->__data[cnt].__invocation_counter = 0; |
| |
| /* It's a regular use. */ |
| result->__data[cnt].__internal_use = 0; |
| #endif |
| |
| /* We use the `mbstate_t' member in DATA. */ |
| result->__data[cnt].__statep = &result->__data[cnt].__state; |
| |
| /* Now see whether we can use any of the transliteration |
| modules for this step. */ |
| for (runp = trans; runp != NULL; runp = runp->next) |
| for (n = 0; n < runp->ncsnames; ++n) |
| if (__strcasecmp_l (steps[cnt].__from_name, |
| runp->csnames[n], _nl_C_locobj_ptr) == 0) |
| { |
| void *data = NULL; |
| |
| /* Match! Now try the initializer. */ |
| if (runp->trans_init_fct == NULL |
| || (runp->trans_init_fct (&data, |
| steps[cnt].__to_name) |
| == __GCONV_OK)) |
| { |
| /* Append at the end of the list. */ |
| struct __gconv_trans_data *newp; |
| struct __gconv_trans_data **lastp; |
| |
| newp = (struct __gconv_trans_data *) |
| malloc (sizeof (struct __gconv_trans_data)); |
| if (newp == NULL) |
| { |
| res = __GCONV_NOMEM; |
| goto bail; |
| } |
| |
| newp->__trans_fct = runp->trans_fct; |
| newp->__trans_context_fct = runp->trans_context_fct; |
| newp->__trans_end_fct = runp->trans_end_fct; |
| newp->__data = data; |
| newp->__next = NULL; |
| |
| lastp = &result->__data[cnt].__trans; |
| while (*lastp != NULL) |
| lastp = &(*lastp)->__next; |
| |
| *lastp = newp; |
| } |
| break; |
| } |
| |
| /* If this is the last step we must not allocate an |
| output buffer. */ |
| if (cnt < nsteps - 1) |
| { |
| result->__data[cnt].__flags = conv_flags; |
| |
| /* Allocate the buffer. */ |
| size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to); |
| |
| result->__data[cnt].__outbuf = malloc (size); |
| if (result->__data[cnt].__outbuf == NULL) |
| { |
| res = __GCONV_NOMEM; |
| goto bail; |
| } |
| |
| result->__data[cnt].__outbufend = |
| result->__data[cnt].__outbuf + size; |
| } |
| else |
| { |
| /* Handle the last entry. */ |
| result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST; |
| |
| break; |
| } |
| } |
| } |
| |
| if (res != __GCONV_OK) |
| { |
| /* Something went wrong. Free all the resources. */ |
| int serrno; |
| bail: |
| serrno = errno; |
| |
| if (result != NULL) |
| { |
| while (cnt-- > 0) |
| { |
| struct __gconv_trans_data *transp; |
| |
| transp = result->__data[cnt].__trans; |
| while (transp != NULL) |
| { |
| struct __gconv_trans_data *curp = transp; |
| transp = transp->__next; |
| |
| if (__builtin_expect (curp->__trans_end_fct != NULL, 0)) |
| curp->__trans_end_fct (curp->__data); |
| |
| free (curp); |
| } |
| |
| free (result->__data[cnt].__outbuf); |
| } |
| |
| free (result); |
| result = NULL; |
| } |
| |
| __gconv_close_transform (steps, nsteps); |
| |
| __set_errno (serrno); |
| } |
| } |
| |
| *handle = result; |
| return res; |
| } |