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