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