1/*
2Copyright 1985, 1986, 1987, 1991, 1998  The Open Group
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the
6"Software"), to deal in the Software without restriction, including
7without limitation the rights to use, copy, modify, merge, publish,
8distribute, sublicense, and/or sell copies of the Software, and to
9permit persons to whom the Software is furnished to do so, subject to
10the following conditions: The above copyright notice and this
11permission notice shall be included in all copies or substantial
12portions of the Software.
13
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
21EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.
22
23
24Except as contained in this notice, the name of The Open Group shall not be
25used in advertising or otherwise to promote the sale, use or other dealings
26in this Software without prior written authorization from The Open Group.
27
28
29X Window System is a trademark of The Open Group
30
31OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
32logo, LBX, X Window System, and Xinerama are trademarks of the Open
33Group. All other trademarks and registered trademarks mentioned herein
34are the property of their respective owners. No right, title or
35interest in or to any trademark, service mark, logo or trade name of
36Sun Microsystems, Inc. or its licensors is granted.
37
38*/
39/*
40 * Copyright (c) 2000, Oracle and/or its affiliates.
41 *
42 * Permission is hereby granted, free of charge, to any person obtaining a
43 * copy of this software and associated documentation files (the "Software"),
44 * to deal in the Software without restriction, including without limitation
45 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
46 * and/or sell copies of the Software, and to permit persons to whom the
47 * Software is furnished to do so, subject to the following conditions:
48 *
49 * The above copyright notice and this permission notice (including the next
50 * paragraph) shall be included in all copies or substantial portions of the
51 * Software.
52 *
53 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
54 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
55 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
56 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
57 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
58 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
59 * DEALINGS IN THE SOFTWARE.
60 */
61
62
63#ifdef HAVE_CONFIG_H
64# include <config.h>
65#else
66# define HAVE_DLFCN_H
67#endif
68
69#include <stdio.h>
70
71#ifdef HAVE_DLFCN_H
72#include <dlfcn.h>
73#endif
74
75#include <ctype.h>
76
77#include "Xlibint.h"
78#include "XlcPublic.h"
79#include "XlcPubI.h"
80#include "reallocarray.h"
81
82#define XI18N_DLREL		2
83
84#define	iscomment(ch)	((ch) == '\0' || (ch) == '#')
85
86typedef enum {
87  XLC_OBJECT,
88  XIM_OBJECT,
89  XOM_OBJECT
90} XI18NDLType;
91
92typedef struct {
93  XI18NDLType type;
94  int	locale_name_len;
95  char *locale_name;
96  char *dl_name;
97  char *open;
98  char *im_register;
99  char *im_unregister;
100  int dl_release;
101  unsigned int refcount;
102  void *dl_module;
103} XI18NObjectsListRec, *XI18NObjectsList;
104
105#define OBJECT_INIT_LEN 8
106#define OBJECT_INC_LEN 4
107static int lc_len = 0;
108static XI18NObjectsListRec *xi18n_objects_list = NULL;
109static int lc_count = 0;
110
111static int
112parse_line(char *line, char **argv, int argsize)
113{
114    int argc = 0;
115    char *p = line;
116
117    while (argc < argsize) {
118	while (isspace(*p)) {
119	    ++p;
120	}
121	if (iscomment(*p)){
122	    break;
123	}
124	argv[argc++] = p;
125	while (!isspace(*p)) {
126	    ++p;
127	}
128	if (iscomment(*p)) {
129	    break;
130	}
131	*p++ = '\0';
132    }
133    return argc;
134}
135
136static char *
137strdup_with_underscore(const char *symbol)
138{
139	char *result;
140
141	if ((result = malloc(strlen(symbol) + 2)) == NULL)
142		return NULL;
143	result[0] = '_';
144	strcpy(result + 1, symbol);
145	return result;
146}
147
148static void *
149try_both_dlsym (void *handle, char *name)
150{
151    void    *ret;
152
153    ret = dlsym (handle, name);
154    if (!ret)
155    {
156	 name = strdup_with_underscore (name);
157	 if (name)
158	 {
159	     ret = dlsym (handle, name);
160	     free (name);
161	 }
162    }
163    return ret;
164}
165
166static void
167resolve_object(char *path, const char *lc_name)
168{
169    char filename[BUFSIZ];
170    FILE *fp;
171    char buf[BUFSIZ];
172
173    if (lc_len == 0) { /* True only for the 1st time */
174      lc_len = OBJECT_INIT_LEN;
175      xi18n_objects_list = Xmallocarray(lc_len, sizeof(XI18NObjectsListRec));
176      if (!xi18n_objects_list) return;
177    }
178    snprintf(filename, sizeof(filename), "%s/%s", path, "XI18N_OBJS");
179    fp = fopen(filename, "r");
180    if (fp == (FILE *)NULL){
181	return;
182    }
183
184    while (fgets(buf, BUFSIZ, fp) != NULL){
185	char *p = buf;
186	int n;
187	char *args[6];
188	while (isspace(*p)){
189	    ++p;
190	}
191	if (iscomment(*p)){
192	    continue;
193	}
194
195	if (lc_count == lc_len) {
196	  int new_len = lc_len + OBJECT_INC_LEN;
197	  XI18NObjectsListRec *tmp =
198              Xreallocarray(xi18n_objects_list, new_len,
199                            sizeof(XI18NObjectsListRec));
200	  if (tmp == NULL)
201	      goto done;
202	  xi18n_objects_list = tmp;
203	  lc_len = new_len;
204	}
205	n = parse_line(p, args, 6);
206
207	if (n == 3 || n == 5) {
208	  if (!strcmp(args[0], "XLC")){
209	    xi18n_objects_list[lc_count].type = XLC_OBJECT;
210	  } else if (!strcmp(args[0], "XOM")){
211	    xi18n_objects_list[lc_count].type = XOM_OBJECT;
212	  } else if (!strcmp(args[0], "XIM")){
213	    xi18n_objects_list[lc_count].type = XIM_OBJECT;
214	  }
215	  xi18n_objects_list[lc_count].dl_name = strdup(args[1]);
216	  xi18n_objects_list[lc_count].open = strdup(args[2]);
217	  xi18n_objects_list[lc_count].dl_release = XI18N_DLREL;
218	  xi18n_objects_list[lc_count].locale_name = strdup(lc_name);
219	  xi18n_objects_list[lc_count].refcount = 0;
220	  xi18n_objects_list[lc_count].dl_module = (void*)NULL;
221	  if (n == 5) {
222	    xi18n_objects_list[lc_count].im_register = strdup(args[3]);
223	    xi18n_objects_list[lc_count].im_unregister = strdup(args[4]);
224	  } else {
225	    xi18n_objects_list[lc_count].im_register = NULL;
226	    xi18n_objects_list[lc_count].im_unregister = NULL;
227	  }
228	  lc_count++;
229	}
230    }
231  done:
232    fclose(fp);
233}
234
235static char*
236__lc_path(const char *dl_name, const char *lc_dir)
237{
238    char *path;
239    size_t len;
240    char *slash_p;
241
242    /*
243     * reject this for possible security issue
244     */
245    if (strstr (dl_name, "../"))
246	return NULL;
247
248    len = (lc_dir ? strlen(lc_dir) : 0 ) +
249	(dl_name ? strlen(dl_name) : 0) + 10;
250#if defined POSTLOCALELIBDIR
251    len += (strlen(POSTLOCALELIBDIR) + 1);
252#endif
253    path = Xmalloc(len + 1);
254
255    if (strchr(dl_name, '/') != NULL) {
256	slash_p = strrchr(lc_dir, '/');
257	*slash_p = '\0';
258    } else
259	slash_p = NULL;
260
261#if defined POSTLOCALELIBDIR
262    snprintf(path, len + 1, "%s/%s/%s.so.2",
263             lc_dir, POSTLOCALELIBDIR, dl_name);
264#else
265    snprintf(path, len + 1, "%s/%s.so.2", lc_dir, dl_name);
266#endif
267
268    if (slash_p != NULL)
269	*slash_p = '/';
270
271    return path;
272}
273
274/* We reference count dlopen() and dlclose() of modules; unfortunately,
275 * since XCloseIM, XCloseOM, XlcClose aren't wrapped, but directly
276 * call the close method of the object, we leak a reference count every
277 * time we open then close a module. Fixing this would require
278 * either creating proxy objects or hooks for close_im/close_om
279 * in XLCd
280 */
281static Bool
282open_object(
283     XI18NObjectsList object,
284     char *lc_dir)
285{
286  char *path;
287
288  if (object->refcount == 0) {
289      path = __lc_path(object->dl_name, lc_dir);
290      if (!path)
291	  return False;
292      object->dl_module = dlopen(path, RTLD_LAZY);
293      Xfree(path);
294
295      if (!object->dl_module)
296	  return False;
297    }
298
299  object->refcount++;
300  return True;
301}
302
303static void *
304fetch_symbol(
305     XI18NObjectsList object,
306     char *symbol)
307{
308    void *result = NULL;
309
310    if (symbol == NULL)
311    	return NULL;
312
313    result = try_both_dlsym(object->dl_module, symbol);
314
315    return result;
316}
317
318static void
319close_object(XI18NObjectsList object)
320{
321  object->refcount--;
322  if (object->refcount == 0)
323    {
324        dlclose(object->dl_module);
325        object->dl_module = NULL;
326    }
327}
328
329
330typedef XLCd (*dynamicLoadProc)(const char *);
331
332XLCd
333_XlcDynamicLoad(const char *lc_name)
334{
335    XLCd lcd = (XLCd)NULL;
336    dynamicLoadProc lc_loader = (dynamicLoadProc)NULL;
337    int count;
338    XI18NObjectsList objects_list;
339    char lc_dir[BUFSIZE], lc_lib_dir[BUFSIZE];
340
341    if (lc_name == NULL) return (XLCd)NULL;
342
343    if (_XlcLocaleDirName(lc_dir, BUFSIZE, lc_name) == NULL)
344        return (XLCd)NULL;
345    if (_XlcLocaleLibDirName(lc_lib_dir, BUFSIZE, lc_name) == NULL)
346	return (XLCd)NULL;
347
348    resolve_object(lc_dir, lc_name);
349    resolve_object(lc_lib_dir, lc_name);
350
351    objects_list = xi18n_objects_list;
352    count = lc_count;
353    for (; count-- > 0; objects_list++) {
354        if (objects_list->type != XLC_OBJECT ||
355	    strcmp(objects_list->locale_name, lc_name)) continue;
356	if (!open_object (objects_list, lc_dir) && \
357            !open_object (objects_list, lc_lib_dir))
358	    continue;
359
360	lc_loader = (dynamicLoadProc)fetch_symbol (objects_list, objects_list->open);
361	if (!lc_loader) continue;
362	lcd = (*lc_loader)(lc_name);
363	if (lcd != (XLCd)NULL) {
364	    break;
365	}
366
367	close_object (objects_list);
368    }
369    return (XLCd)lcd;
370}
371
372
373typedef XIM (*dynamicOpenProcp)(XLCd, Display *, XrmDatabase, char *, char *);
374
375static XIM
376_XDynamicOpenIM(XLCd lcd, Display *display, XrmDatabase rdb,
377		char *res_name, char *res_class)
378{
379  XIM im = (XIM)NULL;
380  char lc_dir[BUFSIZE];
381  char *lc_name;
382  dynamicOpenProcp im_openIM = (dynamicOpenProcp)NULL;
383  int count;
384  XI18NObjectsList objects_list = xi18n_objects_list;
385
386  lc_name = lcd->core->name;
387
388  if (_XlcLocaleLibDirName(lc_dir, BUFSIZE, lc_name) == NULL) return (XIM)0;
389
390  count = lc_count;
391  for (; count-- > 0; objects_list++) {
392    if (objects_list->type != XIM_OBJECT ||
393	strcmp(objects_list->locale_name, lc_name)) continue;
394
395    if (!open_object (objects_list, lc_dir))
396        continue;
397
398    im_openIM = (dynamicOpenProcp)fetch_symbol(objects_list, objects_list->open);
399    if (!im_openIM) continue;
400    im = (*im_openIM)(lcd, display, rdb, res_name, res_class);
401    if (im != (XIM)NULL) {
402        break;
403    }
404
405    close_object (objects_list);
406  }
407  return (XIM)im;
408}
409
410typedef Bool (*dynamicRegisterCBProcp)(
411    XLCd, Display *, XrmDatabase, char *, char *, XIDProc, XPointer);
412
413static Bool
414_XDynamicRegisterIMInstantiateCallback(
415    XLCd	 lcd,
416    Display	*display,
417    XrmDatabase	 rdb,
418    char	*res_name,
419    char        *res_class,
420    XIDProc	 callback,
421    XPointer	 client_data)
422{
423  char lc_dir[BUFSIZE];
424  char *lc_name;
425  dynamicRegisterCBProcp im_registerIM = (dynamicRegisterCBProcp)NULL;
426  Bool ret_flag = False;
427  int count;
428  XI18NObjectsList objects_list = xi18n_objects_list;
429
430  lc_name = lcd->core->name;
431
432  if (_XlcLocaleLibDirName(lc_dir, BUFSIZE, lc_name) == NULL) return False;
433
434  count = lc_count;
435  for (; count-- > 0; objects_list++) {
436    if (objects_list->type != XIM_OBJECT ||
437	strcmp(objects_list->locale_name, lc_name)) continue;
438
439    if (!open_object (objects_list, lc_dir))
440        continue;
441    im_registerIM = (dynamicRegisterCBProcp)fetch_symbol(objects_list,
442					    objects_list->im_register);
443    if (!im_registerIM) continue;
444    ret_flag = (*im_registerIM)(lcd, display, rdb,
445				res_name, res_class,
446				callback, client_data);
447    if (ret_flag) break;
448
449    close_object (objects_list);
450  }
451  return (Bool)ret_flag;
452}
453
454typedef Bool (*dynamicUnregisterProcp)(
455    XLCd, Display *, XrmDatabase, char *, char *, XIDProc, XPointer);
456
457static Bool
458_XDynamicUnRegisterIMInstantiateCallback(
459    XLCd	 lcd,
460    Display	*display,
461    XrmDatabase	 rdb,
462    char	*res_name,
463    char        *res_class,
464    XIDProc	 callback,
465    XPointer	 client_data)
466{
467  char lc_dir[BUFSIZE];
468  const char *lc_name;
469  dynamicUnregisterProcp im_unregisterIM = (dynamicUnregisterProcp)NULL;
470  Bool ret_flag = False;
471  int count;
472  XI18NObjectsList objects_list = xi18n_objects_list;
473
474  lc_name = lcd->core->name;
475  if (_XlcLocaleDirName(lc_dir, BUFSIZE, lc_name) == NULL) return False;
476
477  count = lc_count;
478  for (; count-- > 0; objects_list++) {
479    if (objects_list->type != XIM_OBJECT ||
480	strcmp(objects_list->locale_name, lc_name)) continue;
481
482    if (!objects_list->refcount) /* Must already be opened */
483        continue;
484
485    im_unregisterIM = (dynamicUnregisterProcp)fetch_symbol(objects_list,
486					      objects_list->im_unregister);
487
488    if (!im_unregisterIM) continue;
489    ret_flag = (*im_unregisterIM)(lcd, display, rdb,
490				  res_name, res_class,
491				  callback, client_data);
492    if (ret_flag) {
493        close_object (objects_list); /* opened in RegisterIMInstantiateCallback */
494	break;
495    }
496  }
497  return (Bool)ret_flag;
498}
499
500Bool
501_XInitDynamicIM(XLCd lcd)
502{
503    if(lcd == (XLCd)NULL)
504	return False;
505    lcd->methods->open_im = _XDynamicOpenIM;
506    lcd->methods->register_callback = _XDynamicRegisterIMInstantiateCallback;
507    lcd->methods->unregister_callback = _XDynamicUnRegisterIMInstantiateCallback;
508    return True;
509}
510
511
512typedef XOM (*dynamicIOpenProcp)(
513        XLCd, Display *, XrmDatabase, _Xconst char *, _Xconst char *);
514
515static XOM
516_XDynamicOpenOM(XLCd lcd, Display *display, XrmDatabase rdb,
517		_Xconst char *res_name, _Xconst char *res_class)
518{
519  XOM om = (XOM)NULL;
520  int count;
521  char lc_dir[BUFSIZE];
522  char *lc_name;
523  dynamicIOpenProcp om_openOM = (dynamicIOpenProcp)NULL;
524  XI18NObjectsList objects_list = xi18n_objects_list;
525
526  lc_name = lcd->core->name;
527
528  if (_XlcLocaleLibDirName(lc_dir, BUFSIZE, lc_name) == NULL) return (XOM)0;
529
530  count = lc_count;
531  for (; count-- > 0; objects_list++) {
532    if (objects_list->type != XOM_OBJECT ||
533	strcmp(objects_list->locale_name, lc_name)) continue;
534    if (!open_object (objects_list, lc_dir))
535        continue;
536
537    om_openOM = (dynamicIOpenProcp)fetch_symbol(objects_list, objects_list->open);
538    if (!om_openOM) continue;
539    om = (*om_openOM)(lcd, display, rdb, res_name, res_class);
540    if (om != (XOM)NULL) {
541        break;
542    }
543    close_object(objects_list);
544  }
545  return (XOM)om;
546}
547
548Bool
549_XInitDynamicOM(XLCd lcd)
550{
551    if(lcd == (XLCd)NULL)
552	return False;
553
554    lcd->methods->open_om = _XDynamicOpenOM;
555
556    return True;
557}
558