Prefer https to http for gnu.org and fsf.org URLs
[platform/upstream/glibc.git] / iconv / gconv_open.c
1 /* Find matching transformation algorithms and initialize steps.
2    Copyright (C) 1997-2019 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <https://www.gnu.org/licenses/>.  */
19
20 #include <errno.h>
21 #include <locale.h>
22 #include "../locale/localeinfo.h"
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <gconv_int.h>
27
28
29 /* How many character should be converted in one call?  */
30 #define GCONV_NCHAR_GOAL        8160
31
32
33 int
34 __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
35               int flags)
36 {
37   struct __gconv_step *steps;
38   size_t nsteps;
39   __gconv_t result = NULL;
40   size_t cnt = 0;
41   int res;
42   int conv_flags = 0;
43   const char *errhand;
44   const char *ignore;
45   bool translit = false;
46
47   /* Find out whether any error handling method is specified.  */
48   errhand = strchr (toset, '/');
49   if (errhand != NULL)
50     errhand = strchr (errhand + 1, '/');
51   if (__glibc_likely (errhand != NULL))
52     {
53       if (*++errhand == '\0')
54         errhand = NULL;
55       else
56         {
57           /* Make copy without the error handling description.  */
58           char *newtoset = (char *) alloca (errhand - toset + 1);
59           char *tok;
60           char *ptr = NULL /* Work around a bogus warning */;
61
62           newtoset[errhand - toset] = '\0';
63           toset = memcpy (newtoset, toset, errhand - toset);
64
65           /* Find the appropriate transliteration handlers.  */
66           tok = strdupa (errhand);
67
68           tok = __strtok_r (tok, ",", &ptr);
69           while (tok != NULL)
70             {
71               if (__strcasecmp_l (tok, "TRANSLIT", _nl_C_locobj_ptr) == 0)
72                 translit = true;
73               else if (__strcasecmp_l (tok, "IGNORE", _nl_C_locobj_ptr) == 0)
74                 /* Set the flag to ignore all errors.  */
75                 conv_flags |= __GCONV_IGNORE_ERRORS;
76
77               tok = __strtok_r (NULL, ",", &ptr);
78             }
79         }
80     }
81
82   /* For the source character set we ignore the error handler specification.
83      XXX Is this really always the best?  */
84   ignore = strchr (fromset, '/');
85   if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL
86       && *++ignore != '\0')
87     {
88       char *newfromset = (char *) alloca (ignore - fromset + 1);
89
90       newfromset[ignore - fromset] = '\0';
91       fromset = memcpy (newfromset, fromset, ignore - fromset);
92     }
93
94   /* If the string is empty define this to mean the charset of the
95      currently selected locale.  */
96   if (strcmp (toset, "//") == 0)
97     {
98       const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
99       size_t len = strlen (codeset);
100       char *dest;
101       toset = dest = (char *) alloca (len + 3);
102       memcpy (__mempcpy (dest, codeset, len), "//", 3);
103     }
104   if (strcmp (fromset, "//") == 0)
105     {
106       const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
107       size_t len = strlen (codeset);
108       char *dest;
109       fromset = dest = (char *) alloca (len + 3);
110       memcpy (__mempcpy (dest, codeset, len), "//", 3);
111     }
112
113   res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags);
114   if (res == __GCONV_OK)
115     {
116       /* Allocate room for handle.  */
117       result = (__gconv_t) malloc (sizeof (struct __gconv_info)
118                                    + (nsteps
119                                       * sizeof (struct __gconv_step_data)));
120       if (result == NULL)
121         res = __GCONV_NOMEM;
122       else
123         {
124           /* Remember the list of steps.  */
125           result->__steps = steps;
126           result->__nsteps = nsteps;
127
128           /* Clear the array for the step data.  */
129           memset (result->__data, '\0',
130                   nsteps * sizeof (struct __gconv_step_data));
131
132           /* Call all initialization functions for the transformation
133              step implementations.  */
134           for (cnt = 0; cnt < nsteps; ++cnt)
135             {
136               size_t size;
137
138               /* Would have to be done if we would not clear the whole
139                  array above.  */
140 #if 0
141               /* Reset the counter.  */
142               result->__data[cnt].__invocation_counter = 0;
143
144               /* It's a regular use.  */
145               result->__data[cnt].__internal_use = 0;
146 #endif
147
148               /* We use the `mbstate_t' member in DATA.  */
149               result->__data[cnt].__statep = &result->__data[cnt].__state;
150
151               /* The builtin transliteration handling only
152                  supports the internal encoding.  */
153               if (translit
154                   && __strcasecmp_l (steps[cnt].__from_name,
155                                      "INTERNAL", _nl_C_locobj_ptr) == 0)
156                 conv_flags |= __GCONV_TRANSLIT;
157
158               /* If this is the last step we must not allocate an
159                  output buffer.  */
160               if (cnt < nsteps - 1)
161                 {
162                   result->__data[cnt].__flags = conv_flags;
163
164                   /* Allocate the buffer.  */
165                   size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to);
166
167                   result->__data[cnt].__outbuf = malloc (size);
168                   if (result->__data[cnt].__outbuf == NULL)
169                     {
170                       res = __GCONV_NOMEM;
171                       goto bail;
172                     }
173
174                   result->__data[cnt].__outbufend =
175                     result->__data[cnt].__outbuf + size;
176                 }
177               else
178                 {
179                   /* Handle the last entry.  */
180                   result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST;
181
182                   break;
183                 }
184             }
185         }
186
187       if (res != __GCONV_OK)
188         {
189           /* Something went wrong.  Free all the resources.  */
190           int serrno;
191         bail:
192           serrno = errno;
193
194           if (result != NULL)
195             {
196               while (cnt-- > 0)
197                 free (result->__data[cnt].__outbuf);
198
199               free (result);
200               result = NULL;
201             }
202
203           __gconv_close_transform (steps, nsteps);
204
205           __set_errno (serrno);
206         }
207     }
208
209   *handle = result;
210   return res;
211 }