imLcIm.c revision 0f8248bf
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 = Xmalloc (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    memset (b->tree, 0, sizeof(DefTree));
461    b->mb[0]   = 0;
462    b->wc[0]   = 0;
463    b->utf8[0] = 0;
464
465    m = Xmalloc (msize);
466    memset (m, 0, msize);
467    m->id       = XIM_CACHE_MAGIC;
468    m->version  = XIM_CACHE_VERSION;
469    m->top      = im->private.local.top;
470    m->treeused = b->treeused;
471    m->mbused   = b->mbused;
472    m->wcused   = b->wcused;
473    m->utf8used = b->utf8used;
474    /* Tree first, then wide chars, then the rest due to alignment */
475    m->tree     = msize;
476    m->wc       = msize   + sizeof (DefTree) * m->treeused;
477    m->mb       = m->wc   + sizeof (wchar_t) * m->wcused;
478    m->utf8     = m->mb   +                    m->mbused;
479    m->size     = m->utf8 +                    m->utf8used;
480    strcpy (m->fname, name);
481    strcpy (m->fname+strlen(name)+1, encoding);
482
483    /* This STILL might be racy on NFS */
484    if ( (fd = _XOpenFileMode (cachename, O_WRONLY | O_CREAT | O_EXCL,
485			       0600)) < 0) {
486       Xfree(m);
487       return;
488    }
489    if (! (fp = fdopen (fd, "wb")) ) {
490       close (fd);
491       Xfree(m);
492       return;
493    }
494    fwrite (m, msize, 1, fp);
495    fwrite (im->private.local.base.tree, sizeof(DefTree), m->treeused, fp);
496    fwrite (im->private.local.base.wc,   sizeof(wchar_t), m->wcused,   fp);
497    fwrite (im->private.local.base.mb,   1,               m->mbused,   fp);
498    fwrite (im->private.local.base.utf8, 1,               m->utf8used, fp);
499    if (fclose (fp) != 0)
500	unlink (cachename);
501    _XimCache_mmap = m;
502    memcpy (&_XimCachedDefaultTreeBase, &im->private.local.base,
503	    sizeof (_XimCachedDefaultTreeBase));
504/* fprintf (stderr, "wrote tree %s size %ld to %s\n", name, m->size, cachename); */
505}
506
507#endif
508
509
510static void
511_XimCreateDefaultTree(
512    Xim		im)
513{
514    FILE *fp = NULL;
515    char *name, *tmpname = NULL, *intname;
516    char *cachename = NULL;
517    /* Should use getpwent() instead of $HOME (cross-platform?) */
518    char *home = getenv("HOME");
519    char *cachedir = NULL;
520    char *tmpcachedir = NULL;
521    int   hl = home ? strlen (home) : 0;
522#ifdef COMPOSECACHE
523    const char *encoding = nl_langinfo (CODESET);
524    uid_t euid = geteuid ();
525    gid_t egid = getegid ();
526    int   cachefd = -1;
527    off_t size;
528#endif
529
530    name = getenv("XCOMPOSEFILE");
531    if (name == (char *) NULL) {
532    	if (home != (char *) NULL) {
533            tmpname = name = Xmalloc(hl + 10 + 1);
534            if (name != (char *) NULL) {
535		int fd;
536            	strcpy(name, home);
537            	strcpy(name + hl, "/.XCompose");
538		if ( (fd = _XOpenFile (name, O_RDONLY)) < 0) {
539		    Xfree (name);
540		    name = tmpname = NULL;
541		} else
542		    close (fd);
543            }
544        }
545    }
546
547    if (name == (char *) NULL) {
548        tmpname = name = _XlcFileName(im->core.lcd, COMPOSE_FILE);
549    }
550    intname = name;
551
552#ifdef COMPOSECACHE
553    if (getuid () == euid && getgid () == egid && euid != 0) {
554	char *c;
555	/* Usage: XCOMPOSECACHE=<cachedir>[=<filename>]
556	 * cachedir: directory of cache files
557	 * filename: internally used name for cache file */
558        cachedir = getenv("XCOMPOSECACHE");
559	if (cachedir && (c = strchr (cachedir, '='))) {
560	    tmpcachedir = strdup (cachedir);
561	    intname = tmpcachedir + (c-cachedir) + 1;
562	    tmpcachedir[c-cachedir] = '\0';
563	    cachedir = tmpcachedir;
564	}
565    }
566
567    if (! cachedir) {
568	cachefd = _XimCachedFileName (XIM_GLOBAL_CACHE_DIR, name, intname,
569				      encoding, 0, 1, &cachename, &size);
570	if (cachefd != -1) {
571	    if (_XimLoadCache (cachefd, intname, encoding, size, im)) {
572	        Xfree (tmpcachedir);
573		Xfree (tmpname);
574		Xfree (cachename);
575		close (cachefd);
576		return;
577	    }
578	    close (cachefd);
579	}
580	Xfree (cachename);
581	cachename = NULL;
582    }
583
584    if (getuid () == euid && getgid () == egid && euid != 0 && home) {
585
586	if (! cachedir) {
587	    tmpcachedir = cachedir = Xmalloc (hl+strlen(XIM_HOME_CACHE_DIR)+1);
588	    strcpy (cachedir, home);
589	    strcat (cachedir, XIM_HOME_CACHE_DIR);
590	}
591	cachefd = _XimCachedFileName (cachedir, name, intname, encoding,
592				      euid, 0, &cachename, &size);
593	if (cachefd != -1) {
594	    if (_XimLoadCache (cachefd, intname, encoding, size, im)) {
595	        Xfree (tmpcachedir);
596		Xfree (tmpname);
597		Xfree (cachename);
598		close (cachefd);
599		return;
600	    }
601	    close (cachefd);
602	}
603    }
604#endif
605
606    if (! (fp = _XFopenFile (name, "r"))) {
607	Xfree (tmpcachedir);
608	Xfree (tmpname);
609	Xfree (cachename);
610        return;
611    }
612    _XimParseStringFile(fp, im);
613    fclose(fp);
614
615#ifdef COMPOSECACHE
616    if (cachename) {
617	assert (euid != 0);
618	_XimWriteCachedDefaultTree (intname, encoding, cachename, im);
619    }
620#endif
621
622    Xfree (tmpcachedir);
623    Xfree (tmpname);
624    Xfree (cachename);
625}
626
627static XIMMethodsRec      Xim_im_local_methods = {
628    _XimLocalCloseIM,           /* close */
629    _XimLocalSetIMValues,       /* set_values */
630    _XimLocalGetIMValues,       /* get_values */
631    _XimLocalCreateIC,          /* create_ic */
632    _XimLcctstombs,		/* ctstombs */
633    _XimLcctstowcs,		/* ctstowcs */
634    _XimLcctstoutf8		/* ctstoutf8 */
635};
636
637Bool
638_XimLocalOpenIM(
639    Xim			 im)
640{
641    XLCd		 lcd = im->core.lcd;
642    XlcConv		 conv;
643    XimDefIMValues	 im_values;
644    XimLocalPrivateRec*  private = &im->private.local;
645
646    _XimInitialResourceInfo();
647    if(_XimSetIMResourceList(&im->core.im_resources,
648		 		&im->core.im_num_resources) == False) {
649	goto Open_Error;
650    }
651    if(_XimSetICResourceList(&im->core.ic_resources,
652				&im->core.ic_num_resources) == False) {
653	goto Open_Error;
654    }
655
656    _XimSetIMMode(im->core.im_resources, im->core.im_num_resources);
657
658    _XimGetCurrentIMValues(im, &im_values);
659    if(_XimSetLocalIMDefaults(im, (XPointer)&im_values,
660		im->core.im_resources, im->core.im_num_resources) == False) {
661	goto Open_Error;
662    }
663    _XimSetCurrentIMValues(im, &im_values);
664
665    if (!(conv = _XlcOpenConverter(lcd,	XlcNCompoundText, lcd, XlcNMultiByte)))
666	goto Open_Error;
667    private->ctom_conv = conv;
668
669    if (!(conv = _XlcOpenConverter(lcd,	XlcNCompoundText, lcd, XlcNWideChar)))
670	goto Open_Error;
671    private->ctow_conv = conv;
672
673    if (!(conv = _XlcOpenConverter(lcd,	XlcNCompoundText, lcd, XlcNUtf8String)))
674	goto Open_Error;
675    private->ctoutf8_conv = conv;
676
677    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNMultiByte)))
678	goto Open_Error;
679    private->cstomb_conv = conv;
680
681    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNWideChar)))
682	goto Open_Error;
683    private->cstowc_conv = conv;
684
685    if (!(conv = _XlcOpenConverter(lcd,	XlcNCharSet, lcd, XlcNUtf8String)))
686	goto Open_Error;
687    private->cstoutf8_conv = conv;
688
689    if (!(conv = _XlcOpenConverter(lcd,	XlcNUcsChar, lcd, XlcNChar)))
690	goto Open_Error;
691    private->ucstoc_conv = conv;
692
693    if (!(conv = _XlcOpenConverter(lcd,	XlcNUcsChar, lcd, XlcNUtf8String)))
694	goto Open_Error;
695    private->ucstoutf8_conv = conv;
696
697    private->base.treeused = 1;
698    private->base.mbused   = 1;
699    private->base.wcused   = 1;
700    private->base.utf8used = 1;
701
702    _XimCreateDefaultTree(im);
703
704    im->methods = &Xim_im_local_methods;
705    private->current_ic = (XIC)NULL;
706
707    return(True);
708
709Open_Error :
710    _XimLocalIMFree(im);
711    return(False);
712}
713