1 /* Find matching transformation algorithms and initialize steps.
2 Copyright (C) 1997-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library 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 GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <errno.h>
20 #include <locale.h>
21 #include "../locale/localeinfo.h"
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <gconv_int.h>
26
27
28 /* How many character should be converted in one call? */
29 #define GCONV_NCHAR_GOAL 8160
30
31
32 int
__gconv_open(struct gconv_spec * conv_spec,__gconv_t * handle,int flags)33 __gconv_open (struct gconv_spec *conv_spec, __gconv_t *handle,
34 int flags)
35 {
36 struct __gconv_step *steps;
37 size_t nsteps;
38 __gconv_t result = NULL;
39 size_t cnt = 0;
40 int res;
41 int conv_flags = 0;
42 bool translit = false;
43 char *tocode, *fromcode;
44
45 /* Find out whether any error handling method is specified. */
46 translit = conv_spec->translit;
47
48 if (conv_spec->ignore)
49 conv_flags |= __GCONV_IGNORE_ERRORS;
50
51 tocode = conv_spec->tocode;
52 fromcode = conv_spec->fromcode;
53
54 /* If the string is empty define this to mean the charset of the
55 currently selected locale. */
56 if (strcmp (tocode, "//") == 0)
57 {
58 const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
59 size_t len = strlen (codeset);
60 char *dest;
61 tocode = dest = (char *) alloca (len + 3);
62 memcpy (__mempcpy (dest, codeset, len), "//", 3);
63 }
64 if (strcmp (fromcode, "//") == 0)
65 {
66 const char *codeset = _NL_CURRENT (LC_CTYPE, CODESET);
67 size_t len = strlen (codeset);
68 char *dest;
69 fromcode = dest = (char *) alloca (len + 3);
70 memcpy (__mempcpy (dest, codeset, len), "//", 3);
71 }
72
73 res = __gconv_find_transform (tocode, fromcode, &steps, &nsteps, flags);
74 if (res == __GCONV_OK)
75 {
76 /* Allocate room for handle. */
77 result = (__gconv_t) malloc (sizeof (struct __gconv_info)
78 + (nsteps
79 * sizeof (struct __gconv_step_data)));
80 if (result == NULL)
81 res = __GCONV_NOMEM;
82 else
83 {
84 /* Remember the list of steps. */
85 result->__steps = steps;
86 result->__nsteps = nsteps;
87
88 /* Clear the array for the step data. */
89 memset (result->__data, '\0',
90 nsteps * sizeof (struct __gconv_step_data));
91
92 /* Call all initialization functions for the transformation
93 step implementations. */
94 for (cnt = 0; cnt < nsteps; ++cnt)
95 {
96 size_t size;
97
98 /* Would have to be done if we would not clear the whole
99 array above. */
100 #if 0
101 /* Reset the counter. */
102 result->__data[cnt].__invocation_counter = 0;
103
104 /* It's a regular use. */
105 result->__data[cnt].__internal_use = 0;
106 #endif
107
108 /* We use the `mbstate_t' member in DATA. */
109 result->__data[cnt].__statep = &result->__data[cnt].__state;
110
111 /* The builtin transliteration handling only
112 supports the internal encoding. */
113 if (translit
114 && __strcasecmp_l (steps[cnt].__from_name,
115 "INTERNAL", _nl_C_locobj_ptr) == 0)
116 conv_flags |= __GCONV_TRANSLIT;
117
118 /* If this is the last step we must not allocate an
119 output buffer. */
120 if (cnt < nsteps - 1)
121 {
122 result->__data[cnt].__flags = conv_flags;
123
124 /* Allocate the buffer. */
125 size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to);
126
127 result->__data[cnt].__outbuf = malloc (size);
128 if (result->__data[cnt].__outbuf == NULL)
129 {
130 res = __GCONV_NOMEM;
131 goto bail;
132 }
133
134 result->__data[cnt].__outbufend =
135 result->__data[cnt].__outbuf + size;
136 }
137 else
138 {
139 /* Handle the last entry. */
140 result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST;
141
142 break;
143 }
144 }
145 }
146
147 if (res != __GCONV_OK)
148 {
149 /* Something went wrong. Free all the resources. */
150 int serrno;
151 bail:
152 serrno = errno;
153
154 if (result != NULL)
155 {
156 while (cnt-- > 0)
157 free (result->__data[cnt].__outbuf);
158
159 free (result);
160 result = NULL;
161 }
162
163 __gconv_close_transform (steps, nsteps);
164
165 __set_errno (serrno);
166 }
167 }
168
169 *handle = result;
170 return res;
171 }
172 libc_hidden_def (__gconv_open)
173