imLcIm.c revision 2d67cb4f
1/******************************************************************
2
3          Copyright 1992, 1993, 1994 by FUJITSU LIMITED
4          Copyright 1993 by Digital Equipment Corporation
5
6Permission to use, copy, modify, distribute, and sell this software
7and its documentation for any purpose is hereby granted without fee,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of FUJITSU LIMITED and
11Digital Equipment Corporation not be used in advertising or publicity
12pertaining to distribution of the software without specific, written
13prior permission.  FUJITSU LIMITED and Digital Equipment Corporation
14makes no representations about the suitability of this software for
15any purpose.  It is provided "as is" without express or implied
16warranty.
17
18FUJITSU LIMITED AND DIGITAL EQUIPMENT CORPORATION DISCLAIM ALL
19WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
20WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
21FUJITSU LIMITED AND DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR
22ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
24IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
26THIS SOFTWARE.
27
28  Author:    Takashi Fujiwara     FUJITSU LIMITED
29                               	  fujiwara@a80.tech.yk.fujitsu.co.jp
30  Modifier:  Franky Ling          Digital Equipment Corporation
31	                          frankyling@hgrd01.enet.dec.com
32
33******************************************************************/
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38#include <stdio.h>
39
40#include <X11/Xmd.h>
41#include <X11/Xatom.h>
42#include <X11/Xos.h>
43#include "Xlibint.h"
44#include "Xlcint.h"
45#include "XlcPublic.h"
46#include "XlcPubI.h"
47#include "Ximint.h"
48#include <ctype.h>
49#include <assert.h>
50
51#ifdef COMPOSECACHE
52#  include <sys/types.h>
53#  include <sys/stat.h>
54#  include <sys/mman.h>
55#  include <langinfo.h>
56#endif
57
58
59#ifdef COMPOSECACHE
60
61/* include trailing '/' for cache directory, file prefix otherwise */
62#define XIM_GLOBAL_CACHE_DIR "/var/cache/libx11/compose/"
63#define XIM_HOME_CACHE_DIR   "/.compose-cache/"
64#define XIM_CACHE_MAGIC      ('X' | 'i'<<8 | 'm'<<16 | 'C'<<24)
65#define XIM_CACHE_VERSION    4
66#define XIM_CACHE_TREE_ALIGNMENT 4
67
68#define XIM_HASH_PRIME_1 13
69#define XIM_HASH_PRIME_2 1234096939
70
71typedef INT32 DTStructIndex;
72struct _XimCacheStruct {
73    INT32           id;
74    INT32           version;
75    DTStructIndex   tree;
76    DTStructIndex   mb;
77    DTStructIndex   wc;
78    DTStructIndex   utf8;
79    DTStructIndex   size;
80    DTIndex         top;
81    DTIndex         treeused;
82    DTCharIndex     mbused;
83    DTCharIndex     wcused;
84    DTCharIndex     utf8used;
85    char            fname[1];
86    /* char encoding[1] */
87};
88
89static struct  _XimCacheStruct* _XimCache_mmap = NULL;
90static DefTreeBase _XimCachedDefaultTreeBase;
91static int     _XimCachedDefaultTreeRefcount = 0;
92
93#endif
94
95
96Bool
97_XimCheckIfLocalProcessing(Xim im)
98{
99    FILE        *fp;
100    char        *name;
101
102    if(strcmp(im->core.im_name, "") == 0) {
103	name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
104	if (name != (char *)NULL) {
105	    fp = _XFopenFile (name, "r");
106	    Xfree(name);
107	    if (fp != (FILE *)NULL) {
108		fclose(fp);
109		return(True);
110	    }
111	}
112	return(False);
113    } else if(strcmp(im->core.im_name, "local") == 0 ||
114	      strcmp(im->core.im_name, "none" ) == 0 ) {
115	return(True);
116    }
117    return(False);
118}
119
120static void
121XimFreeDefaultTree(
122    DefTreeBase *b)
123{
124    if (!b) return;
125    if (b->tree == NULL) return;
126#ifdef COMPOSECACHE
127    if (b->tree == _XimCachedDefaultTreeBase.tree) {
128        _XimCachedDefaultTreeRefcount--;
129        /* No deleting, it's a cache after all. */
130        return;
131    }
132#endif
133    Xfree (b->tree);
134    b->tree = NULL;
135    Xfree (b->mb);
136    b->mb   = NULL;
137    Xfree (b->wc);
138    b->wc   = NULL;
139    Xfree (b->utf8);
140    b->utf8 = NULL;
141
142    b->treeused = b->treesize = 0;
143    b->mbused   = b->mbsize   = 0;
144    b->wcused   = b->wcsize   = 0;
145    b->utf8used = b->utf8size = 0;
146}
147
148void
149_XimLocalIMFree(
150    Xim		im)
151{
152    XimFreeDefaultTree(&im->private.local.base);
153    im->private.local.top = 0;
154
155    Xfree(im->core.im_resources);
156    im->core.im_resources = NULL;
157
158    Xfree(im->core.ic_resources);
159    im->core.ic_resources = NULL;
160
161    Xfree(im->core.im_values_list);
162    im->core.im_values_list = NULL;
163
164    Xfree(im->core.ic_values_list);
165    im->core.ic_values_list = NULL;
166
167    Xfree(im->core.styles);
168    im->core.styles = NULL;
169
170    Xfree(im->core.res_name);
171    im->core.res_name = NULL;
172
173    Xfree(im->core.res_class);
174    im->core.res_class = NULL;
175
176    Xfree(im->core.im_name);
177    im->core.im_name = NULL;
178
179    if (im->private.local.ctom_conv) {
180	_XlcCloseConverter(im->private.local.ctom_conv);
181        im->private.local.ctom_conv = NULL;
182    }
183    if (im->private.local.ctow_conv) {
184	_XlcCloseConverter(im->private.local.ctow_conv);
185	im->private.local.ctow_conv = NULL;
186    }
187    if (im->private.local.ctoutf8_conv) {
188	_XlcCloseConverter(im->private.local.ctoutf8_conv);
189	im->private.local.ctoutf8_conv = NULL;
190    }
191    if (im->private.local.cstomb_conv) {
192	_XlcCloseConverter(im->private.local.cstomb_conv);
193        im->private.local.cstomb_conv = NULL;
194    }
195    if (im->private.local.cstowc_conv) {
196	_XlcCloseConverter(im->private.local.cstowc_conv);
197	im->private.local.cstowc_conv = NULL;
198    }
199    if (im->private.local.cstoutf8_conv) {
200	_XlcCloseConverter(im->private.local.cstoutf8_conv);
201	im->private.local.cstoutf8_conv = NULL;
202    }
203    if (im->private.local.ucstoc_conv) {
204	_XlcCloseConverter(im->private.local.ucstoc_conv);
205	im->private.local.ucstoc_conv = NULL;
206    }
207    if (im->private.local.ucstoutf8_conv) {
208	_XlcCloseConverter(im->private.local.ucstoutf8_conv);
209	im->private.local.ucstoutf8_conv = NULL;
210    }
211    return;
212}
213
214static Status
215_XimLocalCloseIM(
216    XIM		xim)
217{
218    Xim		im = (Xim)xim;
219    XIC		ic;
220    XIC		next;
221
222    ic = im->core.ic_chain;
223    im->core.ic_chain = NULL;
224    while (ic) {
225	(*ic->methods->destroy) (ic);
226	next = ic->core.next;
227	Xfree (ic);
228	ic = next;
229    }
230    _XimLocalIMFree(im);
231    _XimDestroyIMStructureList(im);
232    return(True);
233}
234
235char *
236_XimLocalGetIMValues(
237    XIM			 xim,
238    XIMArg		*values)
239{
240    Xim			 im = (Xim)xim;
241    XimDefIMValues	 im_values;
242
243    _XimGetCurrentIMValues(im, &im_values);
244    return(_XimGetIMValueData(im, (XPointer)&im_values, values,
245			im->core.im_resources, im->core.im_num_resources));
246}
247
248char *
249_XimLocalSetIMValues(
250    XIM			 xim,
251    XIMArg		*values)
252{
253    Xim			 im = (Xim)xim;
254    XimDefIMValues	 im_values;
255    char		*name = (char *)NULL;
256
257    _XimGetCurrentIMValues(im, &im_values);
258    name = _XimSetIMValueData(im, (XPointer)&im_values, values,
259		im->core.im_resources, im->core.im_num_resources);
260    _XimSetCurrentIMValues(im, &im_values);
261    return(name);
262}
263
264
265#ifdef COMPOSECACHE
266
267static Bool
268_XimReadCachedDefaultTree(
269    int          fd_cache,
270    const char  *name,
271    const char  *encoding,
272    DTStructIndex size)
273{
274    struct _XimCacheStruct* m;
275    int namelen = strlen (name) + 1;
276    int encodinglen = strlen (encoding) + 1;
277
278    m = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd_cache, 0);
279    if (m == NULL || m == MAP_FAILED)
280        return False;
281    assert (m->id == XIM_CACHE_MAGIC);
282    assert (m->version == XIM_CACHE_VERSION);
283    if (size != m->size ||
284	size < XOffsetOf (struct _XimCacheStruct, fname) + namelen + encodinglen) {
285	fprintf (stderr, "Ignoring broken XimCache %s [%s]\n", name, encoding);
286        munmap (m, size);
287        return False;
288    }
289    if (strncmp (name, m->fname, namelen) != 0) {
290	/* m->fname may *not* be terminated - but who cares here */
291	fprintf (stderr, "Filename hash clash - expected %s, got %s\n",
292		 name, m->fname);
293        munmap (m, size);
294        return False;
295    }
296    if (strncmp (encoding, m->fname + namelen, encodinglen) != 0) {
297	/* m->fname+namelen may *not* be terminated - but who cares here */
298	fprintf (stderr, "Enoding hash clash - expected %s, got %s\n",
299		 encoding, m->fname + namelen);
300        munmap (m, size);
301        return False;
302    }
303    _XimCache_mmap    = m;
304    _XimCachedDefaultTreeBase.tree = (DefTree *) (((char *) m) + m->tree);
305    _XimCachedDefaultTreeBase.mb   =             (((char *) m) + m->mb);
306    _XimCachedDefaultTreeBase.wc   = (wchar_t *) (((char *) m) + m->wc);
307    _XimCachedDefaultTreeBase.utf8 =             (((char *) m) + m->utf8);
308    _XimCachedDefaultTreeBase.treeused = m->treeused;
309    _XimCachedDefaultTreeBase.mbused   = m->mbused;
310    _XimCachedDefaultTreeBase.wcused   = m->wcused;
311    _XimCachedDefaultTreeBase.utf8used = m->utf8used;
312    /* treesize etc. is ignored because only used during parsing */
313    _XimCachedDefaultTreeRefcount = 0;
314/* fprintf (stderr, "read cached tree at %p: %s\n", (void *) m, name); */
315    return True;
316}
317
318static unsigned int strToHash (
319    const char *name)
320{
321    unsigned int hash = 0;
322    while (*name)
323	hash = hash * XIM_HASH_PRIME_1 + *(unsigned const char *)name++;
324    return hash % XIM_HASH_PRIME_2;
325}
326
327
328/* Returns read-only fd of cache file, -1 if none.
329 * Sets *res to cache filename if safe. Sets *size to file size of cache. */
330static int _XimCachedFileName (
331    const char *dir, const char *name,
332    const char *intname, const char *encoding,
333    uid_t uid, int isglobal, char **res, off_t *size)
334{
335    struct stat st_name, st;
336    int    fd;
337    unsigned int len, hash, hash2;
338    struct _XimCacheStruct *m;
339    /* There are some races here with 'dir', but we are either in our own home
340     * or the global cache dir, and not inside some public writable dir */
341/* fprintf (stderr, "XimCachedFileName for dir %s name %s intname %s encoding %s uid %d\n", dir, name, intname, encoding, uid); */
342    if (stat (name, &st_name) == -1 || ! S_ISREG (st_name.st_mode)
343       || stat (dir, &st) == -1 || ! S_ISDIR (st.st_mode) || st.st_uid != uid
344       || (st.st_mode & 0022) != 0000) {
345       *res = NULL;
346       return -1;
347    }
348    len   = strlen (dir);
349    hash  = strToHash (intname);
350    hash2 = strToHash (encoding);
351    *res  = Xmalloc (len + 1 + 27 + 1);  /* Max VERSION 9999 */
352
353    if (len == 0 || dir [len-1] != '/')
354       sprintf (*res, "%s/%c%d_%03x_%08x_%08x", dir, _XimGetMyEndian(),
355		XIM_CACHE_VERSION, (unsigned int)sizeof (DefTree), hash, hash2);
356    else
357       sprintf (*res, "%s%c%d_%03x_%08x_%08x", dir, _XimGetMyEndian(),
358		XIM_CACHE_VERSION, (unsigned int)sizeof (DefTree), hash, hash2);
359
360/* fprintf (stderr, "-> %s\n", *res); */
361    if ( (fd = _XOpenFile (*res, O_RDONLY)) == -1)
362       return -1;
363
364    if (fstat (fd, &st) == -1) {
365       Xfree (*res);
366       *res = NULL;
367       close (fd);
368       return -1;
369    }
370    *size = st.st_size;
371
372    if (! S_ISREG (st.st_mode) || st.st_uid != uid
373       || (st.st_mode & 0022) != 0000 || st.st_mtime <= st_name.st_mtime
374       || (st.st_mtime < time (NULL) - 24*60*60 && ! isglobal)) {
375
376       close (fd);
377       if (unlink (*res) != 0) {
378           Xfree (*res);
379           *res = NULL;                /* cache is not safe */
380       }
381       return -1;
382    }
383
384    m = mmap (NULL, sizeof (struct _XimCacheStruct), PROT_READ, MAP_PRIVATE,
385	      fd, 0);
386    if (m == NULL || m == MAP_FAILED) {
387	close (fd);
388	Xfree (*res);
389	*res = NULL;
390        return -1;
391    }
392    if (*size < sizeof (struct _XimCacheStruct) || m->id != XIM_CACHE_MAGIC) {
393	munmap (m, sizeof (struct _XimCacheStruct));
394	close (fd);
395	fprintf (stderr, "Ignoring broken XimCache %s\n", *res);
396	Xfree (*res);
397	*res = NULL;
398        return -1;
399    }
400    if (m->version != XIM_CACHE_VERSION) {
401	munmap (m, sizeof (struct _XimCacheStruct));
402	close (fd);
403	if (unlink (*res) != 0) {
404	    Xfree (*res);
405	    *res = NULL;                /* cache is not safe */
406	}
407	return -1;
408    }
409    munmap (m, sizeof (struct _XimCacheStruct));
410
411    return fd;
412}
413
414
415static Bool _XimLoadCache (
416    int         fd,
417    const char *name,
418    const char *encoding,
419    off_t       size,
420    Xim         im)
421{
422    if (_XimCache_mmap ||
423       _XimReadCachedDefaultTree (fd, name, encoding, size)) {
424       _XimCachedDefaultTreeRefcount++;
425       memcpy (&im->private.local.base, &_XimCachedDefaultTreeBase,
426	       sizeof (_XimCachedDefaultTreeBase));
427       im->private.local.top = _XimCache_mmap->top;
428       return True;
429    }
430
431    return False;
432}
433
434
435static void
436_XimWriteCachedDefaultTree(
437    const char *name,
438    const char *encoding,
439    const char *cachename,
440    Xim                im)
441{
442    int   fd;
443    FILE *fp;
444    struct _XimCacheStruct *m;
445    int   msize = (XOffsetOf(struct _XimCacheStruct, fname)
446		   + strlen(name) + strlen(encoding) + 2
447		   + XIM_CACHE_TREE_ALIGNMENT-1) & -XIM_CACHE_TREE_ALIGNMENT;
448    DefTreeBase *b = &im->private.local.base;
449
450    if (! b->tree && ! (b->tree = Xcalloc (1, sizeof(DefTree))) )
451	return;
452    if (! b->mb   && ! (b->mb   = Xmalloc (1)) )
453	return;
454    if (! b->wc   && ! (b->wc   = Xmalloc (sizeof(wchar_t))) )
455	return;
456    if (! b->utf8 && ! (b->utf8 = Xmalloc (1)) )
457	return;
458
459    /* First entry is always unused */
460    b->mb[0]   = 0;
461    b->wc[0]   = 0;
462    b->utf8[0] = 0;
463
464    m = Xcalloc (1, msize);
465    m->id       = XIM_CACHE_MAGIC;
466    m->version  = XIM_CACHE_VERSION;
467    m->top      = im->private.local.top;
468    m->treeused = b->treeused;
469    m->mbused   = b->mbused;
470    m->wcused   = b->wcused;
471    m->utf8used = b->utf8used;
472    /* Tree first, then wide chars, then the rest due to alignment */
473    m->tree     = msize;
474    m->wc       = msize   + sizeof (DefTree) * m->treeused;
475    m->mb       = m->wc   + sizeof (wchar_t) * m->wcused;
476    m->utf8     = m->mb   +                    m->mbused;
477    m->size     = m->utf8 +                    m->utf8used;
478    strcpy (m->fname, name);
479    strcpy (m->fname+strlen(name)+1, encoding);
480
481    /* This STILL might be racy on NFS */
482    if ( (fd = _XOpenFileMode (cachename, O_WRONLY | O_CREAT | O_EXCL,
483			       0600)) < 0) {
484       Xfree(m);
485       return;
486    }
487    if (! (fp = fdopen (fd, "wb")) ) {
488       close (fd);
489       Xfree(m);
490       return;
491    }
492    fwrite (m, msize, 1, fp);
493    fwrite (im->private.local.base.tree, sizeof(DefTree), m->treeused, fp);
494    fwrite (im->private.local.base.wc,   sizeof(wchar_t), m->wcused,   fp);
495    fwrite (im->private.local.base.mb,   1,               m->mbused,   fp);
496    fwrite (im->private.local.base.utf8, 1,               m->utf8used, fp);
497    if (fclose (fp) != 0)
498	unlink (cachename);
499    _XimCache_mmap = m;
500    memcpy (&_XimCachedDefaultTreeBase, &im->private.local.base,
501	    sizeof (_XimCachedDefaultTreeBase));
502/* fprintf (stderr, "wrote tree %s size %ld to %s\n", name, m->size, cachename); */
503}
504
505#endif
506
507
508static void
509_XimCreateDefaultTree(
510    Xim		im)
511{
512    FILE *fp = NULL;
513    char *name, *tmpname = NULL, *intname;
514    char *cachename = NULL;
515    /* Should use getpwent() instead of $HOME (cross-platform?) */
516    char *home = getenv("HOME");
517    char *cachedir = NULL;
518    char *tmpcachedir = NULL;
519    int   hl = home ? strlen (home) : 0;
520#ifdef COMPOSECACHE
521    const char *encoding = nl_langinfo (CODESET);
522    uid_t euid = geteuid ();
523    gid_t egid = getegid ();
524    int   cachefd = -1;
525    off_t size;
526#endif
527
528    name = getenv("XCOMPOSEFILE");
529    if (name == (char *) NULL) {
530    	if (home != (char *) NULL) {
531            tmpname = name = Xmalloc(hl + 10 + 1);
532            if (name != (char *) NULL) {
533		int fd;
534            	strcpy(name, home);
535            	strcpy(name + hl, "/.XCompose");
536		if ( (fd = _XOpenFile (name, O_RDONLY)) < 0) {
537		    Xfree (name);
538		    name = tmpname = NULL;
539		} else
540		    close (fd);
541            }
542        }
543    }
544
545    if (name == (char *) NULL) {
546        tmpname = name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
547    }
548    intname = name;
549
550#ifdef COMPOSECACHE
551    if (getuid () == euid && getgid () == egid && euid != 0) {
552	char *c;
553	/* Usage: XCOMPOSECACHE=<cachedir>[=<filename>]
554	 * cachedir: directory of cache files
555	 * filename: internally used name for cache file */
556        cachedir = getenv("XCOMPOSECACHE");
557	if (cachedir && (c = strchr (cachedir, '='))) {
558	    tmpcachedir = strdup (cachedir);
559	    intname = tmpcachedir + (c-cachedir) + 1;
560	    tmpcachedir[c-cachedir] = '\0';
561	    cachedir = tmpcachedir;
562	}
563    }
564
565    if (! cachedir) {
566	cachefd = _XimCachedFileName (XIM_GLOBAL_CACHE_DIR, name, intname,
567				      encoding, 0, 1, &cachename, &size);
568	if (cachefd != -1) {
569	    if (_XimLoadCache (cachefd, intname, encoding, size, im)) {
570	        Xfree (tmpcachedir);
571		Xfree (tmpname);
572		Xfree (cachename);
573		close (cachefd);
574		return;
575	    }
576	    close (cachefd);
577	}
578	Xfree (cachename);
579	cachename = NULL;
580    }
581
582    if (getuid () == euid && getgid () == egid && euid != 0 && home) {
583
584	if (! cachedir) {
585	    tmpcachedir = cachedir = Xmalloc (hl+strlen(XIM_HOME_CACHE_DIR)+1);
586	    strcpy (cachedir, home);
587	    strcat (cachedir, XIM_HOME_CACHE_DIR);
588	}
589	cachefd = _XimCachedFileName (cachedir, name, intname, encoding,
590				      euid, 0, &cachename, &size);
591	if (cachefd != -1) {
592	    if (_XimLoadCache (cachefd, intname, encoding, size, im)) {
593	        Xfree (tmpcachedir);
594		Xfree (tmpname);
595		Xfree (cachename);
596		close (cachefd);
597		return;
598	    }
599	    close (cachefd);
600	}
601    }
602#endif
603
604    if (! (fp = _XFopenFile (name, "r"))) {
605	Xfree (tmpcachedir);
606	Xfree (tmpname);
607	Xfree (cachename);
608        return;
609    }
610    _XimParseStringFile(fp, im);
611    fclose(fp);
612
613#ifdef COMPOSECACHE
614    if (cachename) {
615	assert (euid != 0);
616	_XimWriteCachedDefaultTree (intname, encoding, cachename, im);
617    }
618#endif
619
620    Xfree (tmpcachedir);
621    Xfree (tmpname);
622    Xfree (cachename);
623}
624
625static XIMMethodsRec      Xim_im_local_methods = {
626    _XimLocalCloseIM,           /* close */
627    _XimLocalSetIMValues,       /* set_values */
628    _XimLocalGetIMValues,       /* get_values */
629    _XimLocalCreateIC,          /* create_ic */
630    _XimLcctstombs,		/* ctstombs */
631    _XimLcctstowcs,		/* ctstowcs */
632    _XimLcctstoutf8		/* ctstoutf8 */
633};
634
635Bool
636_XimLocalOpenIM(
637    Xim			 im)
638{
639    XLCd		 lcd = im->core.lcd;
640    XlcConv		 conv;
641    XimDefIMValues	 im_values;
642    XimLocalPrivateRec*  private = &im->private.local;
643
644    _XimInitialResourceInfo();
645    if(_XimSetIMResourceList(&im->core.im_resources,
646		 		&im->core.im_num_resources) == False) {
647	goto Open_Error;
648    }
649    if(_XimSetICResourceList(&im->core.ic_resources,
650				&im->core.ic_num_resources) == False) {
651	goto Open_Error;
652    }
653
654    _XimSetIMMode(im->core.im_resources, im->core.im_num_resources);
655
656    _XimGetCurrentIMValues(im, &im_values);
657    if(_XimSetLocalIMDefaults(im, (XPointer)&im_values,
658		im->core.im_resources, im->core.im_num_resources) == False) {
659	goto Open_Error;
660    }
661    _XimSetCurrentIMValues(im, &im_values);
662
663    if (!(conv = _XlcOpenConverter(lcd,	XlcNCompoundText, lcd, XlcNMultiByte)))
664	goto Open_Error;
665    private->ctom_conv = conv;
666
667    if (!(conv = _XlcOpenConverter(lcd,	XlcNCompoundText, lcd, XlcNWideChar)))
668	goto Open_Error;
669    private->ctow_conv = conv;
670
671    if (!(conv = _XlcOpenConverter(lcd,	XlcNCompoundText, lcd, XlcNUtf8String)))
672	goto Open_Error;
673    private->ctoutf8_conv = conv;
674
675    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNMultiByte)))
676	goto Open_Error;
677    private->cstomb_conv = conv;
678
679    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNWideChar)))
680	goto Open_Error;
681    private->cstowc_conv = conv;
682
683    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNUtf8String)))
684	goto Open_Error;
685    private->cstoutf8_conv = conv;
686
687    if (!(conv = _XlcOpenConverter(lcd,	XlcNUcsChar, lcd, XlcNChar)))
688	goto Open_Error;
689    private->ucstoc_conv = conv;
690
691    if (!(conv = _XlcOpenConverter(lcd,	XlcNUcsChar, lcd, XlcNUtf8String)))
692	goto Open_Error;
693    private->ucstoutf8_conv = conv;
694
695    private->base.treeused = 1;
696    private->base.mbused   = 1;
697    private->base.wcused   = 1;
698    private->base.utf8used = 1;
699
700    _XimCreateDefaultTree(im);
701
702    im->methods = &Xim_im_local_methods;
703    private->current_ic = (XIC)NULL;
704
705    return(True);
706
707Open_Error :
708    _XimLocalIMFree(im);
709    return(False);
710}
711