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