Convert.c revision 249c3046
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
84typedef struct _ConverterRec *ConverterPtr;
85typedef struct _ConverterRec {
86    ConverterPtr	next;
87    XrmRepresentation	from, to;
88    XtTypeConverter	converter;
89    XtDestructor	destructor;
90    unsigned short	num_args;
91    unsigned int	do_ref_count:1;
92    unsigned int	new_style:1;
93    unsigned int	global:1;
94    char		cache_type;
95} ConverterRec;
96
97#define ConvertArgs(p) ((XtConvertArgList)((p)+1))
98
99/* used for old-style type converter cache only */
100static Heap globalHeap = {NULL, NULL, 0};
101
102void _XtSetDefaultConverterTable(
103	ConverterTable *table)
104{
105    register ConverterTable globalConverterTable;
106
107    LOCK_PROCESS;
108    globalConverterTable = _XtGetProcessContext()->globalConverterTable;
109
110    *table = (ConverterTable)
111	__XtCalloc(CONVERTHASHSIZE, (unsigned)sizeof(ConverterPtr));
112    _XtAddDefaultConverters(*table);
113
114    if (globalConverterTable) {
115	ConverterPtr rec;
116	int i;
117	XtCacheType cache_type;
118	for (i = CONVERTHASHSIZE; --i >= 0; ) {
119	    for (rec = *globalConverterTable++; rec; rec = rec->next) {
120		cache_type = rec->cache_type;
121		if (rec->do_ref_count)
122		    cache_type |= XtCacheRefCount;
123	       _XtTableAddConverter(*table, rec->from, rec->to, rec->converter,
124				    ConvertArgs(rec), rec->num_args,
125				    rec->new_style, cache_type,
126				    rec->destructor, True);
127	    }
128  	}
129    }
130    UNLOCK_PROCESS;
131}
132
133void _XtFreeConverterTable(
134	ConverterTable table)
135{
136	register Cardinal i;
137	register ConverterPtr p;
138
139	for (i = 0; i < CONVERTHASHSIZE; i++) {
140	    for (p = table[i]; p; ) {
141		register ConverterPtr next = p->next;
142		XtFree((char*)p);
143		p = next;
144	    }
145	}
146	XtFree((char*)table);
147}
148
149/* Data cache hash table */
150
151typedef struct _CacheRec *CachePtr;
152
153typedef struct _CacheRec {
154    CachePtr	next;
155    XtPointer	tag;
156    int		hash;
157    XtTypeConverter converter;
158    unsigned short num_args;
159    unsigned int conversion_succeeded:1;
160    unsigned int has_ext:1;
161    unsigned int is_refcounted:1;
162    unsigned int must_be_freed:1;
163    unsigned int from_is_value:1;
164    unsigned int to_is_value:1;
165    XrmValue	from;
166    XrmValue	to;
167} CacheRec;
168
169typedef struct _CacheRecExt {
170    CachePtr	*prev;
171    XtDestructor destructor;
172    XtPointer	 closure;
173    long	 ref_count;
174} CacheRecExt;
175
176#define CEXT(p) ((CacheRecExt *)((p)+1))
177#define CARGS(p) ((p)->has_ext ? (XrmValue *)(CEXT(p)+1) : (XrmValue *)((p)+1))
178
179#define CACHEHASHSIZE	256
180#define CACHEHASHMASK	255
181typedef CachePtr CacheHashTable[CACHEHASHSIZE];
182
183static CacheHashTable	cacheHashTable;
184
185void _XtTableAddConverter(
186    ConverterTable	table,
187    XrmRepresentation   from_type,
188    XrmRepresentation   to_type,
189    XtTypeConverter	converter,
190    XtConvertArgList    convert_args,
191    Cardinal		num_args,
192    _XtBoolean		new_style,
193    XtCacheType		cache_type,
194    XtDestructor	destructor,
195    _XtBoolean		global)
196{
197    register ConverterPtr	*pp;
198    register ConverterPtr	p;
199    XtConvertArgList args;
200
201    pp= &table[ProcHash(from_type, to_type) & CONVERTHASHMASK];
202    while ((p = *pp) && (p->from != from_type || p->to != to_type))
203	pp = &p->next;
204
205    if (p) {
206	*pp = p->next;
207	XtFree((char *)p);
208    }
209
210    p = (ConverterPtr) __XtMalloc(sizeof(ConverterRec) +
211				sizeof(XtConvertArgRec) * num_args);
212    p->next	    = *pp;
213    *pp = p;
214    p->from	    = from_type;
215    p->to	    = to_type;
216    p->converter    = converter;
217    p->destructor   = destructor;
218    p->num_args     = num_args;
219    p->global       = global;
220    args = ConvertArgs(p);
221    while (num_args--)
222	*args++ = *convert_args++;
223    p->new_style    = new_style;
224    p->do_ref_count = False;
225    if (destructor || (cache_type & 0xff)) {
226	p->cache_type = cache_type & 0xff;
227	if (cache_type & XtCacheRefCount)
228	    p->do_ref_count = True;
229    } else {
230	p->cache_type = XtCacheNone;
231    }
232}
233
234void XtSetTypeConverter(
235    register _Xconst char* from_type,
236    register _Xconst char* to_type,
237    XtTypeConverter	converter,
238    XtConvertArgList    convert_args,
239    Cardinal		num_args,
240    XtCacheType		cache_type,
241    XtDestructor	destructor
242    )
243{
244    ProcessContext process;
245    XtAppContext app;
246    XrmRepresentation from;
247    XrmRepresentation to;
248
249    LOCK_PROCESS;
250    process = _XtGetProcessContext();
251    app = process->appContextList;
252    from = XrmStringToRepresentation(from_type);
253    to = XrmStringToRepresentation(to_type);
254
255    if (!process->globalConverterTable) {
256	process->globalConverterTable = (ConverterTable)
257	    __XtCalloc(CONVERTHASHSIZE, (unsigned)sizeof(ConverterPtr));
258    }
259    _XtTableAddConverter(process->globalConverterTable, from, to,
260			 converter, convert_args,
261			 num_args, True, cache_type, destructor, True);
262    while (app) {
263	_XtTableAddConverter(app->converterTable, from, to,
264			     converter, convert_args,
265			     num_args, True, cache_type, destructor, True);
266	app = app->next;
267    }
268    UNLOCK_PROCESS;
269}
270
271void XtAppSetTypeConverter(
272    XtAppContext	app,
273    register _Xconst char* from_type,
274    register _Xconst char* to_type,
275    XtTypeConverter	converter,
276    XtConvertArgList    convert_args,
277    Cardinal		num_args,
278    XtCacheType		cache_type,
279    XtDestructor	destructor
280    )
281{
282    LOCK_PROCESS;
283    _XtTableAddConverter(app->converterTable,
284	XrmStringToRepresentation(from_type),
285        XrmStringToRepresentation(to_type),
286	converter, convert_args, num_args,
287	True, cache_type, destructor, False);
288    UNLOCK_PROCESS;
289}
290
291/* old interface */
292void XtAddConverter(
293    register _Xconst char* from_type,
294    register _Xconst char* to_type,
295    XtConverter		converter,
296    XtConvertArgList    convert_args,
297    Cardinal		num_args
298    )
299{
300    ProcessContext process;
301    XtAppContext app;
302    XrmRepresentation from;
303    XrmRepresentation to;
304
305    LOCK_PROCESS;
306    process = _XtGetProcessContext();
307    app = process->appContextList;
308    from = XrmStringToRepresentation(from_type);
309    to = XrmStringToRepresentation(to_type);
310
311    if (!process->globalConverterTable) {
312	process->globalConverterTable = (ConverterTable)
313	    __XtCalloc(CONVERTHASHSIZE, (unsigned)sizeof(ConverterPtr));
314    }
315    _XtTableAddConverter(process->globalConverterTable, from, to,
316			 (XtTypeConverter)converter, convert_args, num_args,
317			 False, XtCacheAll, (XtDestructor)NULL, True);
318    while (app) {
319	_XtTableAddConverter(app->converterTable, from, to,
320			     (XtTypeConverter)converter, convert_args,
321			     num_args, False, XtCacheAll, (XtDestructor)NULL,
322			     True);
323	app = app->next;
324    }
325    UNLOCK_PROCESS;
326}
327
328/* old interface */
329void XtAppAddConverter(
330    XtAppContext	app,
331    register _Xconst char* from_type,
332    register _Xconst char* to_type,
333    XtConverter		converter,
334    XtConvertArgList    convert_args,
335    Cardinal		num_args
336    )
337{
338    LOCK_PROCESS;
339    _XtTableAddConverter(app->converterTable,
340	XrmStringToRepresentation(from_type),
341        XrmStringToRepresentation(to_type),
342	(XtTypeConverter)converter, convert_args, num_args,
343	False, XtCacheAll, (XtDestructor)NULL, False);
344    UNLOCK_PROCESS;
345}
346
347static CachePtr
348CacheEnter(
349    Heap*		    heap,
350    register XtTypeConverter converter,
351    register XrmValuePtr    args,
352    Cardinal		    num_args,
353    XrmValuePtr		    from,
354    XrmValuePtr		    to,
355    Boolean		    succeeded,
356    register int	    hash,
357    Boolean		    do_ref,
358    Boolean		    do_free,
359    XtDestructor	    destructor,
360    XtPointer		    closure)
361{
362    register	CachePtr *pHashEntry;
363    register	CachePtr p;
364    register    Cardinal i;
365
366    LOCK_PROCESS;
367    pHashEntry = &cacheHashTable[hash & CACHEHASHMASK];
368
369    if ((succeeded && destructor) || do_ref) {
370	p = (CachePtr) _XtHeapAlloc(heap, (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, (sizeof(CacheRec) +
381					  num_args * sizeof(XrmValue)));
382	p->has_ext = False;
383    }
384    if (!to->addr)
385	succeeded = False;
386    p->conversion_succeeded = succeeded;
387    p->is_refcounted = do_ref;
388    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 = num_args;
407    if (num_args) {
408	XrmValue *pargs = CARGS(p);
409	for (i = 0; i < num_args; i++) {
410	    pargs[i].size = args[i].size;
411	    pargs[i].addr = (XPointer)_XtHeapAlloc(heap, args[i].size);
412	    XtMemmove(pargs[i].addr, args[i].addr, args[i].size);
413	}
414    }
415    p->to.size = to->size;
416    if (!succeeded) {
417	p->to_is_value = False;
418	p->to.addr = NULL;
419    } else if (to->size <= sizeof(p->to.addr)) {
420	p->to_is_value = True;
421	XtMemmove(&p->to.addr, to->addr, to->size);
422    } else {
423	p->to_is_value = False;
424	p->to.addr = (XPointer)_XtHeapAlloc(heap, to->size);
425	(void) memmove((char *)p->to.addr, (char *)to->addr, to->size);
426    }
427    UNLOCK_PROCESS;
428    return p;
429}
430
431static void FreeCacheRec(
432    XtAppContext app,
433    CachePtr p,
434    CachePtr *prev)
435{
436    LOCK_PROCESS;
437    if (p->has_ext) {
438	if (CEXT(p)->destructor) {
439	    Cardinal num_args = p->num_args;
440	    XrmValue *args = NULL;
441	    XrmValue toc;
442	    if (num_args)
443		args = CARGS(p);
444	    toc.size = p->to.size;
445	    if (p->to_is_value)
446		toc.addr = (XPointer)&p->to.addr;
447	    else
448		toc.addr = p->to.addr;
449	    (*CEXT(p)->destructor) (app, &toc, CEXT(p)->closure, args,
450				    &num_args);
451	}
452	*(CEXT(p)->prev) = p->next;
453	if (p->next && p->next->has_ext)
454	    CEXT(p->next)->prev = CEXT(p)->prev;
455    } else {
456	*prev = p->next;
457	if (p->next && p->next->has_ext)
458	    CEXT(p->next)->prev = prev;
459    }
460    if (p->must_be_freed) {
461	register int i;
462	if (!p->from_is_value)
463	    XtFree(p->from.addr);
464	if ((i = p->num_args)) {
465	    XrmValue *pargs = CARGS(p);
466	    while (i--)
467		XtFree(pargs[i].addr);
468	}
469	if (!p->to_is_value)
470	    XtFree(p->to.addr);
471	XtFree((char*)p);
472    }
473    /* else on private heap; will free entire heap later */
474    UNLOCK_PROCESS;
475}
476
477
478void _XtCacheFlushTag(
479    XtAppContext app,
480    XtPointer	tag)
481{
482    int i;
483    register CachePtr *prev;
484    register CachePtr rec;
485
486    LOCK_PROCESS;
487    for (i = CACHEHASHSIZE; --i >= 0;) {
488	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: %3d  '",
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    register WidgetClass     wc;
535    register Cardinal        i;
536    register XrmResourceList res, *resources;
537
538    for (wc = widget_class; wc; wc = wc->core_class.superclass) {
539	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 = -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 = ((long) converter >> 2) + from->size + *((char *) from->addr);
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((char *)p->from.addr, (char *)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    Cardinal i;
728    Boolean retval;
729
730    if (!cP || ((cP->cache_type == XtCacheNone) && !cP->destructor)) {
731	XtPointer closure;
732	if (cache_ref_return) *cache_ref_return = NULL;
733	retval = (*(XtTypeConverter)converter)
734	    (dpy, args, &num_args, from, to, &closure);
735	return retval;
736    }
737
738    LOCK_PROCESS;
739    /* Try to find cache entry for conversion */
740    hash = ((long)(converter) >> 2) + from->size + *((char *) from->addr);
741    if (from->size > 1) hash += ((char *) from->addr)[1];
742
743    if (cP->cache_type != XtCacheNone) {
744	for (p = cacheHashTable[hash & CACHEHASHMASK]; p; p = p->next){
745	    if ((p->hash == hash)
746	     && (p->converter == converter)
747	     && (p->from.size == from->size)
748	     && !(p->from_is_value ?
749		  XtMemcmp(&p->from.addr, from->addr, from->size) :
750		  memcmp((char *)p->from.addr, (char *)from->addr, from->size))
751	     && (p->num_args == num_args)) {
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	Boolean 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