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