Convert.c revision 72af6995
1/***********************************************************
2Copyright (c) 1993, Oracle and/or its affiliates. All rights reserved.
3
4Permission is hereby granted, free of charge, to any person obtaining a
5copy of this software and associated documentation files (the "Software"),
6to deal in the Software without restriction, including without limitation
7the rights to use, copy, modify, merge, publish, distribute, sublicense,
8and/or sell copies of the Software, and to permit persons to whom the
9Software is furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice (including the next
12paragraph) shall be included in all copies or substantial portions of the
13Software.
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
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                        All Rights Reserved
26
27Permission to use, copy, modify, and distribute this software and its
28documentation for any purpose and without fee is hereby granted,
29provided that the above copyright notice appear in all copies and that
30both that copyright notice and this permission notice appear in
31supporting documentation, and that the name of Digital not be
32used in advertising or publicity pertaining to distribution of the
33software without specific, written prior permission.
34
35DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41SOFTWARE.
42
43******************************************************************/
44
45/*
46
47Copyright 1987, 1988, 1998  The Open Group
48
49Permission to use, copy, modify, distribute, and sell this software and its
50documentation for any purpose is hereby granted without fee, provided that
51the above copyright notice appear in all copies and that both that
52copyright notice and this permission notice appear in supporting
53documentation.
54
55The above copyright notice and this permission notice shall be included in
56all copies or substantial portions of the Software.
57
58THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
59IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
60FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
61OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
62AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
63CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64
65Except as contained in this notice, the name of The Open Group shall not be
66used in advertising or otherwise to promote the sale, use or other dealings
67in this Software without prior written authorization from The Open Group.
68
69*/
70
71#ifdef HAVE_CONFIG_H
72#include <config.h>
73#endif
74#include	"IntrinsicI.h"
75#include	"StringDefs.h"
76#include	"Intrinsic.h"
77
78/* Conversion procedure hash table */
79
80#define CONVERTHASHSIZE	((unsigned)256)
81#define CONVERTHASHMASK	255
82#define ProcHash(from_type, to_type) (2 * (from_type) + to_type)
83#define HashCode(converter, from) (int)(((long)(converter) >> 2) + from->size + *((char *) from->addr))
84
85typedef struct _ConverterRec *ConverterPtr;
86typedef struct _ConverterRec {
87    ConverterPtr	next;
88    XrmRepresentation	from, to;
89    XtTypeConverter	converter;
90    XtDestructor	destructor;
91    unsigned short	num_args;
92    unsigned int	do_ref_count:1;
93    unsigned int	new_style:1;
94    unsigned int	global:1;
95    char		cache_type;
96} ConverterRec;
97
98#define ConvertArgs(p) ((XtConvertArgList)((p)+1))
99
100/* used for old-style type converter cache only */
101static Heap globalHeap = {NULL, NULL, 0};
102
103void _XtSetDefaultConverterTable(
104	ConverterTable *table)
105{
106    register ConverterTable globalConverterTable;
107
108    LOCK_PROCESS;
109    globalConverterTable = _XtGetProcessContext()->globalConverterTable;
110
111    *table = (ConverterTable)
112	__XtCalloc(CONVERTHASHSIZE, (unsigned)sizeof(ConverterPtr));
113    _XtAddDefaultConverters(*table);
114
115    if (globalConverterTable) {
116	ConverterPtr rec;
117	int i;
118	XtCacheType cache_type;
119	for (i = CONVERTHASHSIZE; --i >= 0; ) {
120	    for (rec = *globalConverterTable++; rec; rec = rec->next) {
121		cache_type = rec->cache_type;
122		if (rec->do_ref_count)
123		    cache_type |= XtCacheRefCount;
124	       _XtTableAddConverter(*table, rec->from, rec->to, rec->converter,
125				    ConvertArgs(rec), rec->num_args,
126				    rec->new_style, cache_type,
127				    rec->destructor, True);
128	    }
129  	}
130    }
131    UNLOCK_PROCESS;
132}
133
134void _XtFreeConverterTable(
135	ConverterTable table)
136{
137	register Cardinal i;
138	register ConverterPtr p;
139
140	for (i = 0; i < CONVERTHASHSIZE; i++) {
141	    for (p = table[i]; p; ) {
142		register ConverterPtr next = p->next;
143		XtFree((char*)p);
144		p = next;
145	    }
146	}
147	XtFree((char*)table);
148}
149
150/* Data cache hash table */
151
152typedef struct _CacheRec *CachePtr;
153
154typedef struct _CacheRec {
155    CachePtr	next;
156    XtPointer	tag;
157    int		hash;
158    XtTypeConverter converter;
159    unsigned short num_args;
160    unsigned int conversion_succeeded:1;
161    unsigned int has_ext:1;
162    unsigned int is_refcounted:1;
163    unsigned int must_be_freed:1;
164    unsigned int from_is_value:1;
165    unsigned int to_is_value:1;
166    XrmValue	from;
167    XrmValue	to;
168} CacheRec;
169
170typedef struct _CacheRecExt {
171    CachePtr	*prev;
172    XtDestructor destructor;
173    XtPointer	 closure;
174    long	 ref_count;
175} CacheRecExt;
176
177#define CEXT(p) ((CacheRecExt *)((p)+1))
178#define CARGS(p) ((p)->has_ext ? (XrmValue *)(CEXT(p)+1) : (XrmValue *)((p)+1))
179
180#define CACHEHASHSIZE	256
181#define CACHEHASHMASK	255
182typedef CachePtr CacheHashTable[CACHEHASHSIZE];
183
184static CacheHashTable	cacheHashTable;
185
186void _XtTableAddConverter(
187    ConverterTable	table,
188    XrmRepresentation   from_type,
189    XrmRepresentation   to_type,
190    XtTypeConverter	converter,
191    XtConvertArgList    convert_args,
192    Cardinal		num_args,
193    _XtBoolean		new_style,
194    XtCacheType		cache_type,
195    XtDestructor	destructor,
196    _XtBoolean		global)
197{
198    register ConverterPtr	*pp;
199    register ConverterPtr	p;
200    XtConvertArgList args;
201
202    pp= &table[ProcHash(from_type, to_type) & CONVERTHASHMASK];
203    while ((p = *pp) && (p->from != from_type || p->to != to_type))
204	pp = &p->next;
205
206    if (p) {
207	*pp = p->next;
208	XtFree((char *)p);
209    }
210
211    p = (ConverterPtr) __XtMalloc((Cardinal)(sizeof(ConverterRec) +
212				sizeof(XtConvertArgRec) * num_args));
213    p->next	    = *pp;
214    *pp = p;
215    p->from	    = from_type;
216    p->to	    = to_type;
217    p->converter    = converter;
218    p->destructor   = destructor;
219    p->num_args     = (unsigned short) num_args;
220    XtSetBit(p->global, global);
221    args = ConvertArgs(p);
222    while (num_args--)
223	*args++ = *convert_args++;
224    XtSetBit(p->new_style, new_style);
225    p->do_ref_count = False;
226    if (destructor || (cache_type & 0xff)) {
227	p->cache_type = (char) (cache_type & 0xff);
228	if (cache_type & XtCacheRefCount)
229	    p->do_ref_count = True;
230    } else {
231	p->cache_type = XtCacheNone;
232    }
233}
234
235void XtSetTypeConverter(
236    register _Xconst char* from_type,
237    register _Xconst char* to_type,
238    XtTypeConverter	converter,
239    XtConvertArgList    convert_args,
240    Cardinal		num_args,
241    XtCacheType		cache_type,
242    XtDestructor	destructor
243    )
244{
245    ProcessContext process;
246    XtAppContext app;
247    XrmRepresentation from;
248    XrmRepresentation to;
249
250    LOCK_PROCESS;
251    process = _XtGetProcessContext();
252    app = process->appContextList;
253    from = XrmStringToRepresentation(from_type);
254    to = XrmStringToRepresentation(to_type);
255
256    if (!process->globalConverterTable) {
257	process->globalConverterTable = (ConverterTable)
258	    __XtCalloc(CONVERTHASHSIZE, (unsigned)sizeof(ConverterPtr));
259    }
260    _XtTableAddConverter(process->globalConverterTable, from, to,
261			 converter, convert_args,
262			 num_args, True, cache_type, destructor, True);
263    while (app) {
264	_XtTableAddConverter(app->converterTable, from, to,
265			     converter, convert_args,
266			     num_args, True, cache_type, destructor, True);
267	app = app->next;
268    }
269    UNLOCK_PROCESS;
270}
271
272void XtAppSetTypeConverter(
273    XtAppContext	app,
274    register _Xconst char* from_type,
275    register _Xconst char* to_type,
276    XtTypeConverter	converter,
277    XtConvertArgList    convert_args,
278    Cardinal		num_args,
279    XtCacheType		cache_type,
280    XtDestructor	destructor
281    )
282{
283    LOCK_PROCESS;
284    _XtTableAddConverter(app->converterTable,
285	XrmStringToRepresentation(from_type),
286        XrmStringToRepresentation(to_type),
287	converter, convert_args, num_args,
288	True, cache_type, destructor, False);
289    UNLOCK_PROCESS;
290}
291
292/* old interface */
293void XtAddConverter(
294    register _Xconst char* from_type,
295    register _Xconst char* to_type,
296    XtConverter		converter,
297    XtConvertArgList    convert_args,
298    Cardinal		num_args
299    )
300{
301    ProcessContext process;
302    XtAppContext app;
303    XrmRepresentation from;
304    XrmRepresentation to;
305
306    LOCK_PROCESS;
307    process = _XtGetProcessContext();
308    app = process->appContextList;
309    from = XrmStringToRepresentation(from_type);
310    to = XrmStringToRepresentation(to_type);
311
312    if (!process->globalConverterTable) {
313	process->globalConverterTable = (ConverterTable)
314	    __XtCalloc(CONVERTHASHSIZE, (unsigned)sizeof(ConverterPtr));
315    }
316    _XtTableAddConverter(process->globalConverterTable, from, to,
317			 (XtTypeConverter)converter, convert_args, num_args,
318			 False, XtCacheAll, (XtDestructor)NULL, True);
319    while (app) {
320	_XtTableAddConverter(app->converterTable, from, to,
321			     (XtTypeConverter)converter, convert_args,
322			     num_args, False, XtCacheAll, (XtDestructor)NULL,
323			     True);
324	app = app->next;
325    }
326    UNLOCK_PROCESS;
327}
328
329/* old interface */
330void XtAppAddConverter(
331    XtAppContext	app,
332    register _Xconst char* from_type,
333    register _Xconst char* to_type,
334    XtConverter		converter,
335    XtConvertArgList    convert_args,
336    Cardinal		num_args
337    )
338{
339    LOCK_PROCESS;
340    _XtTableAddConverter(app->converterTable,
341	XrmStringToRepresentation(from_type),
342        XrmStringToRepresentation(to_type),
343	(XtTypeConverter)converter, convert_args, num_args,
344	False, XtCacheAll, (XtDestructor)NULL, False);
345    UNLOCK_PROCESS;
346}
347
348static CachePtr
349CacheEnter(
350    Heap*		    heap,
351    register XtTypeConverter converter,
352    register XrmValuePtr    args,
353    Cardinal		    num_args,
354    XrmValuePtr		    from,
355    XrmValuePtr		    to,
356    Boolean		    succeeded,
357    register int	    hash,
358    Boolean		    do_ref,
359    Boolean		    do_free,
360    XtDestructor	    destructor,
361    XtPointer		    closure)
362{
363    register	CachePtr *pHashEntry;
364    register	CachePtr p;
365
366    LOCK_PROCESS;
367    pHashEntry = &cacheHashTable[hash & CACHEHASHMASK];
368
369    if ((succeeded && destructor) || do_ref) {
370	p = (CachePtr) _XtHeapAlloc(heap, (Cardinal) (sizeof(CacheRec) +
371					   sizeof(CacheRecExt) +
372					   num_args * sizeof(XrmValue)));
373	CEXT(p)->prev = pHashEntry;
374	CEXT(p)->destructor = succeeded ? destructor : NULL;
375	CEXT(p)->closure = closure;
376	CEXT(p)->ref_count = 1;
377	p->has_ext = True;
378    }
379    else {
380	p = (CachePtr)_XtHeapAlloc(heap, (Cardinal) (sizeof(CacheRec) +
381					  num_args * sizeof(XrmValue)));
382	p->has_ext = False;
383    }
384    if (!to->addr)
385	succeeded = False;
386    XtSetBit(p->conversion_succeeded, succeeded);
387    XtSetBit(p->is_refcounted, do_ref);
388    XtSetBit(p->must_be_freed, do_free);
389    p->next	    = *pHashEntry;
390    if (p->next && p->next->has_ext)
391	CEXT(p->next)->prev = &p->next;
392
393    *pHashEntry     = p;
394    p->tag	    = (XtPointer)heap;
395    p->hash	    = hash;
396    p->converter    = converter;
397    p->from.size    = from->size;
398    if (from->size <= sizeof(p->from.addr)) {
399	p->from_is_value = True;
400	XtMemmove(&p->from.addr, from->addr, from->size);
401    } else {
402	p->from_is_value = False;
403	p->from.addr = (XPointer)_XtHeapAlloc(heap, from->size);
404	(void) memmove((char *)p->from.addr, (char *)from->addr, from->size);
405    }
406    p->num_args = (unsigned short) num_args;
407    if (num_args && args) {
408	XrmValue *pargs = CARGS(p);
409        register    Cardinal i;
410	for (i = 0; i < num_args; i++) {
411	    pargs[i].size = args[i].size;
412	    pargs[i].addr = (XPointer)_XtHeapAlloc(heap, args[i].size);
413	    XtMemmove(pargs[i].addr, args[i].addr, args[i].size);
414	}
415    }
416    p->to.size = to->size;
417    if (!succeeded) {
418	p->to_is_value = False;
419	p->to.addr = NULL;
420    } else if (to->size <= sizeof(p->to.addr)) {
421	p->to_is_value = True;
422	XtMemmove(&p->to.addr, to->addr, to->size);
423    } else {
424	p->to_is_value = False;
425	p->to.addr = (XPointer)_XtHeapAlloc(heap, to->size);
426	(void) memmove((char *)p->to.addr, (char *)to->addr, to->size);
427    }
428    UNLOCK_PROCESS;
429    return p;
430}
431
432static void FreeCacheRec(
433    XtAppContext app,
434    CachePtr p,
435    CachePtr *prev)
436{
437    LOCK_PROCESS;
438    if (p->has_ext) {
439	if (CEXT(p)->destructor) {
440	    Cardinal num_args = p->num_args;
441	    XrmValue *args = NULL;
442	    XrmValue toc;
443	    if (num_args)
444		args = CARGS(p);
445	    toc.size = p->to.size;
446	    if (p->to_is_value)
447		toc.addr = (XPointer)&p->to.addr;
448	    else
449		toc.addr = p->to.addr;
450	    (*CEXT(p)->destructor) (app, &toc, CEXT(p)->closure, args,
451				    &num_args);
452	}
453	*(CEXT(p)->prev) = p->next;
454	if (p->next && p->next->has_ext)
455	    CEXT(p->next)->prev = CEXT(p)->prev;
456    } else if (prev) {
457	*prev = p->next;
458	if (p->next && p->next->has_ext)
459	    CEXT(p->next)->prev = prev;
460    }
461    if (p->must_be_freed) {
462	register int i;
463	if (!p->from_is_value)
464	    XtFree(p->from.addr);
465	if ((i = p->num_args)) {
466	    XrmValue *pargs = CARGS(p);
467	    while (i--)
468		XtFree(pargs[i].addr);
469	}
470	if (!p->to_is_value)
471	    XtFree(p->to.addr);
472	XtFree((char*)p);
473    }
474    /* else on private heap; will free entire heap later */
475    UNLOCK_PROCESS;
476}
477
478
479void _XtCacheFlushTag(
480    XtAppContext app,
481    XtPointer	tag)
482{
483    int i;
484    register CachePtr rec;
485
486    LOCK_PROCESS;
487    for (i = CACHEHASHSIZE; --i >= 0;) {
488        register CachePtr *prev = &cacheHashTable[i];
489	while ((rec = *prev)) {
490	    if (rec->tag == tag)
491		FreeCacheRec(app, rec, prev);
492	    else
493		prev = &rec->next;
494	}
495    }
496    UNLOCK_PROCESS;
497}
498
499#ifdef DEBUG
500#include	<stdio.h>
501
502void _XtConverterCacheStats(void)
503{
504    register Cardinal i;
505    register CachePtr p;
506    register Cardinal entries;
507
508    LOCK_PROCESS;
509    for (i = 0; i < CACHEHASHSIZE; i++) {
510	p = cacheHashTable[i];
511	if (p) {
512	    for (entries = 0; p; p = p->next) {
513		entries++;
514	    }
515	    (void) fprintf(stdout, "Index: %4d  Entries: %d\n", i, entries);
516	    for (p = cacheHashTable[i]; p; p = p->next) {
517		(void) fprintf(stdout, "    Size: %3d  Refs: %3ld  '",
518			       p->from.size,
519			       p->has_ext ? CEXT(p)->ref_count : 0);
520		(void) fprintf(stdout, "'\n");
521	    }
522	    (void) fprintf(stdout, "\n");
523	}
524    }
525    UNLOCK_PROCESS;
526}
527#endif /*DEBUG*/
528
529static Boolean ResourceQuarkToOffset(
530    WidgetClass widget_class,
531    XrmName     name,
532    Cardinal    *offset)
533{
534    WidgetClass     wc;
535    Cardinal        i;
536    XrmResourceList res;
537
538    for (wc = widget_class; wc; wc = wc->core_class.superclass) {
539	XrmResourceList *resources = (XrmResourceList*) wc->core_class.resources;
540	for (i = 0; i < wc->core_class.num_resources; i++, resources++) {
541	    res = *resources;
542	    if (res->xrm_name == name) {
543		*offset = (Cardinal) (-res->xrm_offset - 1);
544		return True;
545	    }
546	} /* for i in resources */
547    } /* for wc in widget classes */
548    (*offset) = 0;
549    return False;
550}
551
552
553static void ComputeArgs(
554    Widget		widget,
555    XtConvertArgList    convert_args,
556    Cardinal		num_args,
557    XrmValuePtr		args)
558{
559    register Cardinal   i;
560    Cardinal		offset;
561    String              params[1];
562    Cardinal		num_params = 1;
563    Widget		ancestor = NULL;
564
565    for (i = 0; i < num_args; i++) {
566	args[i].size = convert_args[i].size;
567	switch (convert_args[i].address_mode) {
568	case XtAddress:
569	    args[i].addr = convert_args[i].address_id;
570	    break;
571
572	case XtBaseOffset:
573	    args[i].addr =
574		(XPointer)((char *)widget + (long)convert_args[i].address_id);
575	    break;
576
577	case XtWidgetBaseOffset:
578	    if (!ancestor) {
579		if (XtIsWidget(widget))
580		    ancestor = widget;
581		else
582		    ancestor = _XtWindowedAncestor(widget);
583	    }
584
585	    args[i].addr =
586		(XPointer)((char *)ancestor + (long)convert_args[i].address_id);
587	    break;
588
589	case XtImmediate:
590	    args[i].addr = (XPointer) &(convert_args[i].address_id);
591	    break;
592
593	case XtProcedureArg:
594	    (*(XtConvertArgProc)convert_args[i].address_id)
595		(widget, &convert_args[i].size, &args[i]);
596	    break;
597
598	case XtResourceString:
599	    /* Convert in place for next usage */
600	    convert_args[i].address_mode = XtResourceQuark;
601	    convert_args[i].address_id =
602	       (XtPointer)(long)XrmStringToQuark((String)convert_args[i].address_id);
603	    /* Fall through */
604
605	case XtResourceQuark:
606	    if (! ResourceQuarkToOffset(widget->core.widget_class,
607		    (XrmQuark)(long) convert_args[i].address_id, &offset)) {
608		params[0]=
609                  XrmQuarkToString((XrmQuark)(long) convert_args[i].address_id);
610               XtAppWarningMsg(XtWidgetToApplicationContext(widget),
611		    "invalidResourceName","computeArgs",XtCXtToolkitError,
612		    "Cannot find resource name %s as argument to conversion",
613                     params,&num_params);
614		offset = 0;
615	    }
616	    args[i].addr = (XPointer)((char *)widget + offset);
617	    break;
618	default:
619	    params[0] = XtName(widget);
620	    XtAppWarningMsg(XtWidgetToApplicationContext(widget),
621		"invalidAddressMode", "computeArgs", XtCXtToolkitError,
622		"Conversion arguments for widget '%s' contain an unsupported address mode",
623			params,&num_params);
624	    args[i].addr = NULL;
625	    args[i].size = 0;
626	} /* switch */
627    } /* for */
628} /* ComputeArgs */
629
630void XtDirectConvert(
631    XtConverter     converter,
632    XrmValuePtr     args,
633    Cardinal	    num_args,
634    register XrmValuePtr from,
635    XrmValuePtr     to)
636{
637    register CachePtr   p;
638    register int	hash;
639    register Cardinal   i;
640
641    LOCK_PROCESS;
642    /* Try to find cache entry for conversion */
643    hash = HashCode(converter, from);
644    if (from->size > 1) hash += ((char *) from->addr)[1];
645
646    for (p = cacheHashTable[hash & CACHEHASHMASK]; p; p = p->next) {
647	if ((p->hash == hash)
648	 && (p->converter == (XtTypeConverter)converter)
649	 && (p->from.size == from->size)
650	 && !(p->from_is_value ?
651	      XtMemcmp(&p->from.addr, from->addr, from->size) :
652	      memcmp((const void *)p->from.addr, (const void *)from->addr, from->size))
653         && (p->num_args == num_args)) {
654	    if ((i = num_args)) {
655		XrmValue *pargs = CARGS(p);
656		/* Are all args the same data ? */
657		while (i) {
658		    i--; /* do not move to while test, broken compilers */
659		    if (pargs[i].size != args[i].size ||
660			XtMemcmp(pargs[i].addr, args[i].addr, args[i].size)) {
661			i++;
662			break;
663		    }
664		}
665	    }
666	    if (!i) {
667		/* Perfect match */
668		to->size = p->to.size;
669		if (p->to_is_value)
670		    to->addr = (XPointer)&p->to.addr;
671		else
672		    to->addr = p->to.addr;
673		UNLOCK_PROCESS;
674		return;
675	    }
676	}
677    }
678
679    /* Didn't find it, call converter procedure and entry result in cache */
680    (*to).size = 0;
681    (*to).addr = NULL;
682    (*converter)(args, &num_args, from, to);
683    /* This memory can never be freed since we don't know the Display
684     * or app context from which to compute the persistance */
685    {
686	CacheEnter(&globalHeap, (XtTypeConverter)converter, args, num_args,
687		   from, to, (to->addr != NULL), hash, False, False,
688		   (XtDestructor)NULL, NULL);
689    }
690    UNLOCK_PROCESS;
691}
692
693
694static ConverterPtr GetConverterEntry(
695    XtAppContext app,
696    XtTypeConverter converter)
697{
698    Cardinal entry;
699    register ConverterPtr cP;
700    ConverterTable converterTable;
701
702    LOCK_PROCESS;
703    converterTable = app->converterTable;
704    cP = NULL;
705    for (entry = 0; (entry < CONVERTHASHSIZE) && !cP; entry++) {
706	cP = converterTable[entry];
707	while (cP && (cP->converter != converter)) cP = cP->next;
708    }
709    UNLOCK_PROCESS;
710    return cP;
711}
712
713
714static Boolean
715CallConverter(
716    Display*	    dpy,
717    XtTypeConverter converter,
718    XrmValuePtr     args,
719    Cardinal	    num_args,
720    register XrmValuePtr from,
721    XrmValuePtr     to,
722    XtCacheRef	    *cache_ref_return,
723    register ConverterPtr cP)
724{
725    CachePtr p;
726    int	hash;
727    Boolean retval;
728
729    if (!cP || ((cP->cache_type == XtCacheNone) && !cP->destructor)) {
730	XtPointer closure;
731	if (cache_ref_return) *cache_ref_return = NULL;
732	retval = (*(XtTypeConverter)converter)
733	    (dpy, args, &num_args, from, to, &closure);
734	return retval;
735    }
736
737    LOCK_PROCESS;
738    /* Try to find cache entry for conversion */
739    hash = HashCode(converter, from);
740    if (from->size > 1) hash += ((char *) from->addr)[1];
741
742    if (cP->cache_type != XtCacheNone) {
743	for (p = cacheHashTable[hash & CACHEHASHMASK]; p; p = p->next){
744	    if ((p->hash == hash)
745	     && (p->converter == converter)
746	     && (p->from.size == from->size)
747	     && !(p->from_is_value ?
748		  XtMemcmp(&p->from.addr, from->addr, from->size) :
749		  memcmp((const void *)p->from.addr, (const void *)from->addr, from->size))
750	     && (p->num_args == num_args)) {
751		Cardinal i;
752		if ((i = num_args)) {
753		    XrmValue *pargs = CARGS(p);
754		    /* Are all args the same data ? */
755		    while (i) {
756			i--; /* do not move to while test, broken compilers */
757			if (pargs[i].size != args[i].size ||
758			    XtMemcmp(pargs[i].addr, args[i].addr, args[i].size)){
759			    i++;
760			    break;
761			}
762		    }
763		}
764		if (!i) {
765		    /* Perfect match */
766		    if (p->conversion_succeeded) {
767			if (to->addr) {	/* new-style call */
768			    if (to->size < p->to.size) {
769				to->size = p->to.size;
770				UNLOCK_PROCESS;
771				return False;
772			    }
773			    to->size = p->to.size;
774			    if (p->to_is_value) {
775				XtMemmove(to->addr, &p->to.addr,
776					  to->size);
777			    } else {
778				(void) memmove((char *)to->addr,
779					       (char *)p->to.addr, to->size);
780			    }
781			} else {	/* old-style call */
782			    to->size = p->to.size;
783			    if (p->to_is_value)
784				to->addr = (XPointer)&p->to.addr;
785			    else
786				to->addr = p->to.addr;
787			}
788		    }
789		    if (p->is_refcounted) {
790			CEXT(p)->ref_count++;
791			if (cache_ref_return)
792			    *cache_ref_return = (XtCacheRef)p;
793			else
794			    p->is_refcounted = False;
795		    }
796		    else {
797			if (cache_ref_return)
798			    *cache_ref_return = NULL;
799		    }
800		    retval = (p->conversion_succeeded);
801		    UNLOCK_PROCESS;
802		    return retval;
803		}
804	    }
805	}
806    }
807
808    /* No cache entry, call converter procedure and enter result in cache */
809    {
810	Heap *heap;
811	XtPointer closure = NULL;
812	unsigned int supplied_size = to->size;
813	Boolean do_ref = cP->do_ref_count && cache_ref_return;
814	Boolean do_free = False;
815	retval =
816	    (*(XtTypeConverter)converter)(dpy, args, &num_args, from, to, &closure);
817
818	if (retval == False && supplied_size < to->size) {
819	    /* programmer error: caller must allocate sufficient storage */
820	    if (cache_ref_return)
821		*cache_ref_return = NULL;
822	    UNLOCK_PROCESS;
823	    return False;
824	}
825
826	if ((cP->cache_type == XtCacheNone) || do_ref) {
827	    heap = NULL;
828	    do_free = True;
829	}
830	else if (cP->cache_type == XtCacheByDisplay)
831	    heap = &_XtGetPerDisplay(dpy)->heap;
832	else if (cP->global)
833	    heap = &globalHeap;
834	else
835	    heap = &XtDisplayToApplicationContext(dpy)->heap;
836
837	p = CacheEnter(heap, converter, args, num_args, from, to, retval,
838		       hash, do_ref, do_free, cP->destructor, closure);
839	if (do_ref)
840	    *cache_ref_return = (XtCacheRef)p;
841	else if (cache_ref_return)
842	    *cache_ref_return = NULL;
843	UNLOCK_PROCESS;
844	return retval;
845    }
846}
847
848Boolean
849XtCallConverter(
850    Display*	    dpy,
851    XtTypeConverter converter,
852    XrmValuePtr     args,
853    Cardinal	    num_args,
854    register XrmValuePtr from,
855    XrmValuePtr     to,
856    XtCacheRef	    *cache_ref_return)
857{
858    ConverterPtr cP;
859    Boolean retval;
860    XtAppContext app = XtDisplayToApplicationContext(dpy);
861
862    LOCK_APP(app);
863    if ((cP = GetConverterEntry(app, converter)) == NULL) {
864	XtAppSetTypeConverter(XtDisplayToApplicationContext(dpy),
865			      "_XtUnk1", "_XtUnk2",
866			      converter, NULL, 0,
867			      XtCacheAll, NULL);
868	cP = GetConverterEntry(app, converter);
869    }
870    retval = CallConverter(dpy, converter, args, num_args, from, to,
871			    cache_ref_return, cP);
872    UNLOCK_APP(app);
873    return retval;
874}
875
876Boolean _XtConvert(
877             Widget		widget,
878    register XrmRepresentation	from_type,
879	     XrmValuePtr	from,
880    register XrmRepresentation	to_type,
881    register XrmValuePtr	to,
882    XtCacheRef			*cache_ref_return)
883{
884    XtAppContext	app = XtWidgetToApplicationContext(widget);
885    register ConverterPtr	p;
886    Cardinal		num_args;
887    XrmValue		*args;
888
889    /* Look for type converter */
890    LOCK_PROCESS;
891    p = app->converterTable[ProcHash(from_type, to_type) & CONVERTHASHMASK];
892    for (; p; p = p->next) {
893	if (from_type == p->from && to_type == p->to) {
894	    Boolean retval = False;
895	    /* Compute actual arguments from widget and arg descriptor */
896	    num_args = p->num_args;
897	    if (num_args != 0) {
898		args = (XrmValue*)
899		    ALLOCATE_LOCAL( num_args * sizeof (XrmValue) );
900		if (!args) _XtAllocError("alloca");
901		ComputeArgs(widget, ConvertArgs(p), num_args, args);
902	    } else args = NULL;
903	    if (p->new_style) {
904		retval =
905		    CallConverter(XtDisplayOfObject(widget),
906				     p->converter, args, num_args,
907				     from, to, cache_ref_return, p);
908	    }
909	    else { /* is old-style (non-display) converter */
910		XrmValue tempTo;
911		XtDirectConvert((XtConverter)p->converter, args, num_args,
912				from, &tempTo);
913		if (cache_ref_return)
914		    *cache_ref_return = NULL;
915		if (tempTo.addr) {
916		    if (to->addr) {	/* new-style caller */
917			if (to->size >= tempTo.size) {
918			    if (to_type == _XtQString)
919				*(String*)(to->addr) = tempTo.addr;
920			    else {
921				XtMemmove(to->addr, tempTo.addr,
922					  tempTo.size);
923			    }
924			    retval = True;
925			}
926			to->size = tempTo.size;
927		    } else {		/* old-style caller */
928			*to = tempTo;
929			retval = True;
930		    }
931		}
932	    }
933	    if (args) DEALLOCATE_LOCAL( (XtPointer)args );
934	    UNLOCK_PROCESS;
935	    return retval;
936	}
937    }
938
939    {
940	String params[2];
941	Cardinal num_params = 2;
942	params[0] = XrmRepresentationToString(from_type);
943	params[1] = XrmRepresentationToString(to_type);
944	XtAppWarningMsg(app, "typeConversionError", "noConverter", XtCXtToolkitError,
945	     "No type converter registered for '%s' to '%s' conversion.",
946             params, &num_params);
947    }
948    UNLOCK_PROCESS;
949    return False;
950}
951
952void XtConvert(
953    Widget	widget,
954    _Xconst char* from_type_str,
955    XrmValuePtr	from,
956    _Xconst char* to_type_str,
957    XrmValuePtr	to)
958{
959    XrmQuark    from_type, to_type;
960    WIDGET_TO_APPCON(widget);
961
962    LOCK_APP(app);
963    from_type = XrmStringToRepresentation(from_type_str);
964    to_type = XrmStringToRepresentation(to_type_str);
965    if (from_type != to_type) {
966	/*  It's not safe to ref count these resources, 'cause we
967	    don't know what older clients may have assumed about
968	    the resource lifetimes.
969	XtCacheRef ref;
970	*/
971	to->addr = NULL;
972	to->size = 0;
973	_XtConvert(widget, from_type, from, to_type, to, /*&ref*/ NULL);
974	/*
975	if (ref) {
976	    XtAddCallback( widget, XtNdestroyCallback,
977			   XtCallbackReleaseCacheRef, (XtPointer)ref );
978	}
979	*/
980    }
981    else
982	(*to) = *from;
983    UNLOCK_APP(app);
984}
985
986Boolean XtConvertAndStore(
987    Widget	object,
988    _Xconst char* from_type_str,
989    XrmValuePtr	from,
990    _Xconst char* to_type_str,
991    XrmValuePtr	to)
992{
993    XrmQuark    from_type, to_type;
994    WIDGET_TO_APPCON(object);
995
996    LOCK_APP(app);
997    LOCK_PROCESS;
998    from_type = XrmStringToRepresentation(from_type_str);
999    to_type = XrmStringToRepresentation(to_type_str);
1000    if (from_type != to_type) {
1001	static XtPointer local_valueP = NULL;
1002	static Cardinal local_valueS = 128;
1003	XtCacheRef ref;
1004	Boolean local = False;
1005	do {
1006	    if (!to->addr) {
1007		if (!local_valueP)
1008		    local_valueP = _XtHeapAlloc(&globalHeap, local_valueS);
1009		to->addr = local_valueP;
1010		to->size = local_valueS;
1011		local = True;
1012	    }
1013	    if (!_XtConvert(object, from_type, from, to_type, to, &ref)) {
1014		if (local && (to->size > local_valueS)) {
1015		    to->addr =
1016			local_valueP = _XtHeapAlloc(&globalHeap, to->size);
1017		    local_valueS = to->size;
1018		    continue;
1019		} else {
1020		    if (local) {
1021			to->addr = NULL;
1022			to->size = 0;
1023		    }
1024		    UNLOCK_PROCESS;
1025		    UNLOCK_APP(app);
1026		    return False;
1027		}
1028	    }
1029	    if (ref) {
1030		XtAddCallback( object, XtNdestroyCallback,
1031			       XtCallbackReleaseCacheRef, (XtPointer)ref );
1032	    }
1033	    UNLOCK_PROCESS;
1034	    UNLOCK_APP(app);
1035	    return True;
1036	} while (local /* && local_valueS < to->size */);
1037    }
1038    if (to->addr) {
1039	if (to->size < from->size) {
1040	    to->size = from->size;
1041	    UNLOCK_PROCESS;
1042	    UNLOCK_APP(app);
1043	    return False;
1044	}
1045	(void) memmove(to->addr, from->addr, from->size );
1046	to->size = from->size;
1047    } else			/* from_type == to_type */
1048	*to = *from;
1049    UNLOCK_PROCESS;
1050    UNLOCK_APP(app);
1051    return True;
1052}
1053
1054void XtAppReleaseCacheRefs(
1055    XtAppContext app,
1056    XtCacheRef *refs)
1057{
1058    register CachePtr *r;
1059    register CachePtr p;
1060
1061    LOCK_APP(app);
1062    LOCK_PROCESS;
1063    for (r = (CachePtr*)refs; (p = *r); r++) {
1064	if (p->is_refcounted && --(CEXT(p)->ref_count) == 0) {
1065	    FreeCacheRec(app, p, NULL);
1066	}
1067    }
1068    UNLOCK_PROCESS;
1069    UNLOCK_APP(app);
1070}
1071
1072
1073/* ARGSUSED */
1074void XtCallbackReleaseCacheRefList(
1075    Widget widget,		/* unused */
1076    XtPointer closure,
1077    XtPointer call_data)	/* unused */
1078{
1079    XtAppReleaseCacheRefs( XtWidgetToApplicationContext(widget),
1080			   (XtCacheRef*)closure );
1081    XtFree(closure);
1082}
1083
1084
1085/* ARGSUSED */
1086void XtCallbackReleaseCacheRef(
1087    Widget widget,		/* unused */
1088    XtPointer closure,
1089    XtPointer call_data)	/* unused */
1090{
1091    XtCacheRef cache_refs[2];
1092    cache_refs[0] = (XtCacheRef)closure;
1093    cache_refs[1] = NULL;
1094    XtAppReleaseCacheRefs( XtWidgetToApplicationContext(widget), cache_refs );
1095}
1096