1 /* Find matching transformation algorithms and initialize steps.
2    Copyright (C) 1997, 1998, 1999, 2000, 2001 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, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20 
21 #include <errno.h>
22 #include <locale.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <gconv_int.h>
27 
28 int
29 internal_function
__gconv_open(const char * toset,const char * fromset,__gconv_t * handle,int flags)30 __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
31 	      int flags)
32 {
33   struct __gconv_step *steps;
34   size_t nsteps;
35   __gconv_t result = NULL;
36   size_t cnt = 0;
37   int res;
38   int conv_flags = 0;
39   const char *errhand;
40   const char *ignore;
41   struct trans_struct *trans = NULL;
42   char old_locale[20], *old_locale_p;
43   char *old, *new;
44   size_t len;
45 
46   /* Find out whether any error handling method is specified.  */
47   errhand = strchr (toset, '/');
48   if (errhand != NULL)
49     errhand = strchr (errhand + 1, '/');
50   if (__builtin_expect (errhand != NULL, 1))
51     {
52       if (*++errhand == '\0')
53 	errhand = NULL;
54       else
55 	{
56 	  /* Make copy without the error handling description.  */
57 	  char *newtoset = (char *) alloca (errhand - toset + 1);
58 	  char *tok;
59 	  char *ptr;
60 
61 	  newtoset[errhand - toset] = '\0';
62 	  toset = memcpy (newtoset, toset, errhand - toset);
63 
64 	  /* Find the appropriate transliteration handlers.  */
65           old = (char *)(errhand);
66           len = strlen (old) + 1;
67           new = (char *) alloca (len);
68           tok = (char *) memcpy (new, old, len);
69 
70 	  tok = strtok_r (tok, ",", &ptr);
71 
72           /* Set locale to default C locale. */
73           old_locale_p = setlocale(LC_ALL, "C");
74           strncpy(old_locale, old_locale_p, 20);
75 
76 	  while (tok != NULL)
77 	    {
78 	      if (strcasecmp (tok, "TRANSLIT") == 0)
79 		{
80 		  /* It's the builtin transliteration handling.  We only
81 		     support it for working on the internal encoding.  */
82 		  static const char *internal_trans_names[1] = { "INTERNAL" };
83 		  struct trans_struct *lastp = NULL;
84 		  struct trans_struct *runp;
85 
86 		  for (runp = trans; runp != NULL; runp = runp->next)
87 		    if (runp->trans_fct == __gconv_transliterate)
88 		      break;
89 		    else
90 		      lastp = runp;
91 
92 		  if (runp == NULL)
93 		    {
94 		      struct trans_struct *newp;
95 
96 		      newp = (struct trans_struct *) alloca (sizeof (*newp));
97 		      memset (newp, '\0', sizeof (*newp));
98 
99 		      /* We leave the `name' field zero to signal that
100 			 this is an internal transliteration step.  */
101 		      newp->csnames = internal_trans_names;
102 		      newp->ncsnames = 1;
103 		      newp->trans_fct = __gconv_transliterate;
104 
105 		      if (lastp == NULL)
106 			trans = newp;
107 		      else
108 			lastp->next = newp;
109 		    }
110 		}
111 	      else if (strcasecmp (tok, "IGNORE") == 0)
112 		/* Set the flag to ignore all errors.  */
113 		conv_flags |= __GCONV_IGNORE_ERRORS;
114 	      else
115 		{
116 		  /* `tok' is possibly a module name.  We'll see later
117 		     whether we can find it.  But first see that we do
118 		     not already a module of this name.  */
119 		  struct trans_struct *lastp = NULL;
120 		  struct trans_struct *runp;
121 
122 		  for (runp = trans; runp != NULL; runp = runp->next)
123 		    if (runp->name != NULL
124 			&& strcasecmp (tok, runp->name) == 0)
125 		      break;
126 		    else
127 		      lastp = runp;
128 
129 		  if (runp == NULL)
130 		    {
131 		      struct trans_struct *newp;
132 
133 		      newp = (struct trans_struct *) alloca (sizeof (*newp));
134 		      memset (newp, '\0', sizeof (*newp));
135 		      newp->name = tok;
136 
137 		      if (lastp == NULL)
138 			trans = newp;
139 		      else
140 			lastp->next = newp;
141 		    }
142 		}
143 
144 	      tok = strtok_r (NULL, ",", &ptr);
145 	    }
146 	}
147     }
148 
149   /* For the source character set we ignore the error handler specification.
150      XXX Is this really always the best?  */
151   ignore = strchr (fromset, '/');
152   if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL
153       && *++ignore != '\0')
154     {
155       char *newfromset = (char *) alloca (ignore - fromset + 1);
156 
157       newfromset[ignore - fromset] = '\0';
158       fromset = memcpy (newfromset, fromset, ignore - fromset);
159     }
160 
161   res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags);
162   if (res == __GCONV_OK)
163     {
164       /* Find the modules.  */
165       struct trans_struct *lastp = NULL;
166       struct trans_struct *runp;
167 
168       for (runp = trans; runp != NULL; runp = runp->next)
169 	{
170 	  if (runp->name == NULL
171 	      || __builtin_expect (__gconv_translit_find (runp), 0) == 0)
172 	    lastp = runp;
173 	  else
174 	    /* This means we haven't found the module.  Remove it.  */
175 	    (lastp == NULL ? trans : lastp->next) = runp->next;
176 	}
177 
178       /* Allocate room for handle.  */
179       result = (__gconv_t) malloc (sizeof (struct __gconv_info)
180 				   + (nsteps
181 				      * sizeof (struct __gconv_step_data)));
182       if (result == NULL)
183 	res = __GCONV_NOMEM;
184       else
185 	{
186 	  size_t n;
187 
188 	  /* Remember the list of steps.  */
189 	  result->__steps = steps;
190 	  result->__nsteps = nsteps;
191 
192 	  /* Clear the array for the step data.  */
193 	  memset (result->__data, '\0',
194 		  nsteps * sizeof (struct __gconv_step_data));
195 
196 	  /* Call all initialization functions for the transformation
197 	     step implementations.  */
198 	  for (cnt = 0; cnt < nsteps; ++cnt)
199 	    {
200 	      size_t size;
201 
202 	      /* Would have to be done if we would not clear the whole
203                  array above.  */
204 #if 0
205 	      /* Reset the counter.  */
206 	      result->__data[cnt].__invocation_counter = 0;
207 
208 	      /* It's a regular use.  */
209 	      result->__data[cnt].__internal_use = 0;
210 #endif
211 
212 	      /* We use the `mbstate_t' member in DATA.  */
213 	      result->__data[cnt].__statep = &result->__data[cnt].__state;
214 
215 	      /* Now see whether we can use any of the transliteration
216 		 modules for this step.  */
217 	      for (runp = trans; runp != NULL; runp = runp->next)
218 		for (n = 0; n < runp->ncsnames; ++n)
219 		  if (strcasecmp (steps[cnt].__from_name, runp->csnames[n]) == 0)
220 		    {
221 		      void *data = NULL;
222 
223 		      /* Match!  Now try the initializer.  */
224 		      if (runp->trans_init_fct == NULL
225 			  || (runp->trans_init_fct (&data,
226 						    steps[cnt].__to_name)
227 			      == __GCONV_OK))
228 			{
229 			  /* Append at the end of the list.  */
230 			  struct __gconv_trans_data *newp;
231 			  struct __gconv_trans_data **lastp;
232 
233 			  newp = (struct __gconv_trans_data *)
234 			    malloc (sizeof (struct __gconv_trans_data));
235 			  if (newp == NULL)
236 			    {
237 			      res = __GCONV_NOMEM;
238 			      goto bail;
239 			    }
240 
241 			  newp->__trans_fct = runp->trans_fct;
242 			  newp->__trans_context_fct = runp->trans_context_fct;
243 			  newp->__trans_end_fct = runp->trans_end_fct;
244 			  newp->__data = data;
245 			  newp->__next = NULL;
246 
247 			  lastp = &result->__data[cnt].__trans;
248 			  while (*lastp != NULL)
249 			    lastp = &(*lastp)->__next;
250 
251 			  *lastp = newp;
252 			}
253 		      break;
254 		    }
255 
256 	      /* If this is the last step we must not allocate an
257 		 output buffer.  */
258 	      if (cnt < nsteps - 1)
259 		{
260 		  result->__data[cnt].__flags = conv_flags;
261 
262 		  /* Allocate the buffer.  */
263 		  size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to);
264 
265 		  result->__data[cnt].__outbuf = (char *) malloc (size);
266 		  if (result->__data[cnt].__outbuf == NULL)
267 		    {
268 		      res = __GCONV_NOMEM;
269 		      goto bail;
270 		    }
271 
272 		  result->__data[cnt].__outbufend =
273 		    result->__data[cnt].__outbuf + size;
274 		}
275 	      else
276 		{
277 		  /* Handle the last entry.  */
278 		  result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST;
279 
280 		  break;
281 		}
282 	    }
283 	}
284 
285       if (res != __GCONV_OK)
286 	{
287 	  /* Something went wrong.  Free all the resources.  */
288 	  int serrno;
289 	bail:
290 	  serrno = errno;
291 
292 	  if (result != NULL)
293 	    {
294 	      while (cnt-- > 0)
295 		{
296 		  struct __gconv_trans_data *transp;
297 
298 		  transp = result->__data[cnt].__trans;
299 		  while (transp != NULL)
300 		    {
301 		      struct __gconv_trans_data *curp = transp;
302 		      transp = transp->__next;
303 
304 		      if (__builtin_expect (curp->__trans_end_fct != NULL, 0))
305 			curp->__trans_end_fct (curp->__data);
306 
307 		      free (curp);
308 		    }
309 
310 		  free (result->__data[cnt].__outbuf);
311 		}
312 
313 	      free (result);
314 	      result = NULL;
315 	    }
316 
317 	  __gconv_close_transform (steps, nsteps);
318 
319 	  __set_errno (serrno);
320 	}
321     }
322 
323   *handle = result;
324   setlocale(LC_ALL, old_locale);
325   return res;
326 }
327