/***********************************************************
Copyright (c) 1993, Oracle and/or its affiliates. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Digital not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

/*

Copyright 1987, 1988, 1994, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.

*/

#define INTRINSIC_C

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "IntrinsicI.h"
#include "VarargsI.h"           /* for geoTattler */
#ifndef NO_IDENTIFY_WINDOWS
#include <X11/Xatom.h>
#endif
#ifndef VMS
#include <sys/stat.h>
#endif                          /* VMS */
#ifdef WIN32
#include <direct.h>             /* for _getdrives() */
#endif

#include <stdio.h>
#include <stdlib.h>

String XtCXtToolkitError = "XtToolkitError";

Boolean
XtIsSubclass(Widget widget, WidgetClass myWidgetClass)
{
    register WidgetClass w;
    Boolean retval = FALSE;

    WIDGET_TO_APPCON(widget);

    LOCK_APP(app);
    LOCK_PROCESS;
    for (w = widget->core.widget_class; w != NULL; w = w->core_class.superclass)
        if (w == myWidgetClass) {
            retval = TRUE;
            break;
        }
    UNLOCK_PROCESS;
    UNLOCK_APP(app);
    return retval;
}                               /* XtIsSubclass */

Boolean
_XtCheckSubclassFlag(Widget object, _XtXtEnum flag)
{
    Boolean retval;

    LOCK_PROCESS;
    if (object->core.widget_class->core_class.class_inited & flag)
        retval = TRUE;
    else
        retval = FALSE;
    UNLOCK_PROCESS;
    return retval;
} /*_XtVerifySubclass */

Boolean
_XtIsSubclassOf(Widget object,
                WidgetClass myWidgetClass,
                WidgetClass superClass,
                _XtXtEnum flag)
{
    LOCK_PROCESS;
    if (!(object->core.widget_class->core_class.class_inited & flag)) {
        UNLOCK_PROCESS;
        return False;
    }
    else {
        register WidgetClass c = object->core.widget_class;

        while (c != superClass) {
            if (c == myWidgetClass) {
                UNLOCK_PROCESS;
                return True;
            }
            c = c->core_class.superclass;
        }
        UNLOCK_PROCESS;
        return False;
    }
} /*_XtIsSubclassOf */

XtPointer
XtGetClassExtension(WidgetClass object_class,
                    Cardinal byte_offset,
                    XrmQuark type, long version, Cardinal record_size)
{
    ObjectClassExtension ext;

    LOCK_PROCESS;

    ext = *(ObjectClassExtension *) ((char *) object_class + byte_offset);
    while (ext && (ext->record_type != type || ext->version < version
                   || ext->record_size < record_size)) {
        ext = (ObjectClassExtension) ext->next_extension;
    }

    UNLOCK_PROCESS;
    return (XtPointer) ext;
}

static void
ComputeWindowAttributes(Widget widget,
                        XtValueMask *value_mask,
                        XSetWindowAttributes *values)
{
    XtExposeProc expose;

    *value_mask = CWEventMask | CWColormap;
    (*values).event_mask = (long) XtBuildEventMask(widget);
    (*values).colormap = widget->core.colormap;
    if (widget->core.background_pixmap != XtUnspecifiedPixmap) {
        *value_mask |= CWBackPixmap;
        (*values).background_pixmap = widget->core.background_pixmap;
    }
    else {
        *value_mask |= CWBackPixel;
        (*values).background_pixel = widget->core.background_pixel;
    }
    if (widget->core.border_pixmap != XtUnspecifiedPixmap) {
        *value_mask |= CWBorderPixmap;
        (*values).border_pixmap = widget->core.border_pixmap;
    }
    else {
        *value_mask |= CWBorderPixel;
        (*values).border_pixel = widget->core.border_pixel;
    }
    LOCK_PROCESS;
    expose = widget->core.widget_class->core_class.expose;
    UNLOCK_PROCESS;
    if (expose == (XtExposeProc) NULL) {
        /* Try to avoid redisplay upon resize by making bit_gravity the same
           as the default win_gravity */
        *value_mask |= CWBitGravity;
        (*values).bit_gravity = NorthWestGravity;
    }
}                               /* ComputeWindowAttributes */

static void
CallChangeManaged(register Widget widget)
{
    register Cardinal i;
    XtWidgetProc change_managed;
    register WidgetList children;
    int managed_children = 0;

    register CompositePtr cpPtr;
    register CompositePartPtr clPtr;

    if (XtIsComposite(widget)) {
        cpPtr = (CompositePtr) &((CompositeWidget) widget)->composite;
        clPtr = (CompositePartPtr) &((CompositeWidgetClass)
                                      widget->core.
                                      widget_class)->composite_class;
    }
    else
        return;

    children = cpPtr->children;
    LOCK_PROCESS;
    change_managed = clPtr->change_managed;
    UNLOCK_PROCESS;

    /* CallChangeManaged for all children */
    for (i = cpPtr->num_children; i != 0; --i) {
        CallChangeManaged(children[i - 1]);
        if (XtIsManaged(children[i - 1]))
            managed_children++;
    }

    if (change_managed != NULL && managed_children != 0) {
        CALLGEOTAT(_XtGeoTrace(widget, "Call \"%s\"[%d,%d]'s changemanaged\n",
                               XtName(widget),
                               widget->core.width, widget->core.height));
        (*change_managed) (widget);
    }
}                               /* CallChangeManaged */

static void
MapChildren(CompositePart *cwp)
{
    Cardinal i;
    WidgetList children;

    children = cwp->children;
    for (i = 0; i < cwp->num_children; i++) {
        Widget child = children[i];

        if (XtIsWidget(child)) {
            if (child->core.managed && child->core.mapped_when_managed) {
                XtMapWidget(children[i]);
            }
        }
    }
}                               /* MapChildren */

static Boolean
ShouldMapAllChildren(CompositePart *cwp)
{
    Cardinal i;
    WidgetList children;

    children = cwp->children;
    for (i = 0; i < cwp->num_children; i++) {
        Widget child = children[i];

        if (XtIsWidget(child)) {
            if (XtIsRealized(child) && (!(child->core.managed
                                          && child->core.
                                          mapped_when_managed))) {
                return False;
            }
        }
    }

    return True;
}                               /* ShouldMapAllChildren */

static void
RealizeWidget(Widget widget)
{
    XtValueMask value_mask;
    XSetWindowAttributes values;
    XtRealizeProc realize;
    Window window;
    Display *display;
    String class_name;
    Widget hookobj;

    if (!XtIsWidget(widget) || XtIsRealized(widget))
        return;
    display = XtDisplay(widget);

    _XtInstallTranslations(widget);

    ComputeWindowAttributes(widget, &value_mask, &values);
    LOCK_PROCESS;
    realize = widget->core.widget_class->core_class.realize;
    class_name = widget->core.widget_class->core_class.class_name;
    UNLOCK_PROCESS;
    if (realize == NULL)
        XtAppErrorMsg(XtWidgetToApplicationContext(widget),
                      "invalidProcedure", "realizeProc", XtCXtToolkitError,
                      "No realize class procedure defined", NULL, NULL);
    else {
        CALLGEOTAT(_XtGeoTrace(widget, "Call \"%s\"[%d,%d]'s realize proc\n",
                               XtName(widget),
                               widget->core.width, widget->core.height));
        (*realize) (widget, &value_mask, &values);
    }
    window = XtWindow(widget);
    hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
    if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
        XtChangeHookDataRec call_data;

        call_data.type = XtHrealizeWidget;
        call_data.widget = widget;
        XtCallCallbackList(hookobj,
                           ((HookObject) hookobj)->hooks.changehook_callbacks,
                           (XtPointer) &call_data);
    }
#ifndef NO_IDENTIFY_WINDOWS
    if (_XtGetPerDisplay(display)->appContext->identify_windows) {
        int len_nm, len_cl;
        char *s;

        len_nm = widget->core.name ? (int) strlen(widget->core.name) : 0;
        len_cl = (int) strlen(class_name);
        s = __XtMalloc((unsigned) (len_nm + len_cl + 2));
        s[0] = '\0';
        if (len_nm)
            strcpy(s, widget->core.name);
        strcpy(s + len_nm + 1, class_name);
        XChangeProperty(display, window,
                        XInternAtom(display, "_MIT_OBJ_CLASS",
                                    False),
                        XA_STRING, 8, PropModeReplace, (unsigned char *) s,
                        len_nm + len_cl + 2);
        XtFree(s);
    }
#endif
#ifdef notdef
    _XtRegisterAsyncHandlers(widget);
#endif
    /* (re)register any grabs extant in the translations */
    _XtRegisterGrabs(widget);
    /* reregister any grabs added with XtGrab{Button,Key} */
    _XtRegisterPassiveGrabs(widget);
    XtRegisterDrawable(display, window, widget);

    _XtExtensionSelect(widget);

    if (XtIsComposite(widget)) {
        Cardinal i;
        CompositePart *cwp = &(((CompositeWidget) widget)->composite);
        WidgetList children = cwp->children;

        /* Realize all children */
        for (i = cwp->num_children; i != 0; --i) {
            RealizeWidget(children[i - 1]);
        }
        /* Map children that are managed and mapped_when_managed */

        if (cwp->num_children != 0) {
            if (ShouldMapAllChildren(cwp)) {
                XMapSubwindows(display, window);
            }
            else {
                MapChildren(cwp);
            }
        }
    }

    /* If this is the application's popup shell, map it */
    if (widget->core.parent == NULL && widget->core.mapped_when_managed) {
        XtMapWidget(widget);
    }
}                               /* RealizeWidget */

void
XtRealizeWidget(Widget widget)
{
    WIDGET_TO_APPCON(widget);

    LOCK_APP(app);
    if (XtIsRealized(widget)) {
        UNLOCK_APP(app);
        return;
    }
    CallChangeManaged(widget);
    RealizeWidget(widget);
    UNLOCK_APP(app);
}                               /* XtRealizeWidget */

static void
UnrealizeWidget(Widget widget)
{
    CompositeWidget cw;

    if (!XtIsWidget(widget) || !XtIsRealized(widget))
        return;

    /* If this is the application's popup shell, unmap it? */
    /* no, the window is being destroyed */

    /* Recurse on children */
    if (XtIsComposite(widget)) {
        Cardinal i;
        WidgetList children;

        cw = (CompositeWidget) widget;
        children = cw->composite.children;
        /* Unrealize all children */
        for (i = cw->composite.num_children; i != 0; --i) {
            UnrealizeWidget(children[i - 1]);
        }
        /* Unmap children that are managed and mapped_when_managed? */
        /* No, it's ok to be managed and unrealized as long as your parent */
        /* is unrealized. XtUnrealize widget makes sure the "top" widget */
        /* is unmanaged, we can ignore all descendents */
    }

    if (XtHasCallbacks(widget, XtNunrealizeCallback) == XtCallbackHasSome)
        XtCallCallbacks(widget, XtNunrealizeCallback, NULL);

    /* Unregister window */
    XtUnregisterDrawable(XtDisplay(widget), XtWindow(widget));

    /* Remove Event Handlers */
    /* remove grabs. Happens automatically when window is destroyed. */

    /* Destroy X Window, done at outer level with one request */
    widget->core.window = None;

    /* Removing the event handler here saves having to keep track if
     * the translation table is changed while the widget is unrealized.
     */
    _XtRemoveTranslations(widget);
}                               /* UnrealizeWidget */

void
XtUnrealizeWidget(Widget widget)
{
    Window window;
    Widget hookobj;

    WIDGET_TO_APPCON(widget);

    LOCK_APP(app);
    window = XtWindow(widget);
    if (!XtIsRealized(widget)) {
        UNLOCK_APP(app);
        return;
    }
    if (widget->core.managed && widget->core.parent != NULL)
        XtUnmanageChild(widget);
    UnrealizeWidget(widget);
    if (window != None)
        XDestroyWindow(XtDisplay(widget), window);
    hookobj = XtHooksOfDisplay(XtDisplayOfObject(widget));
    if (XtHasCallbacks(hookobj, XtNchangeHook) == XtCallbackHasSome) {
        XtChangeHookDataRec call_data;

        call_data.type = XtHunrealizeWidget;
        call_data.widget = widget;
        XtCallCallbackList(hookobj,
                           ((HookObject) hookobj)->hooks.changehook_callbacks,
                           (XtPointer) &call_data);
    }
    UNLOCK_APP(app);
}                               /* XtUnrealizeWidget */

void
XtCreateWindow(Widget widget,
               unsigned int window_class,
               Visual *visual,
               XtValueMask value_mask,
               XSetWindowAttributes *attributes)
{
    XtAppContext app = XtWidgetToApplicationContext(widget);

    LOCK_APP(app);
    if (widget->core.window == None) {
        if (widget->core.width == 0 || widget->core.height == 0) {
            Cardinal count = 1;

            XtAppErrorMsg(app,
                          "invalidDimension", "xtCreateWindow",
                          XtCXtToolkitError,
                          "Widget %s has zero width and/or height",
                          &widget->core.name, &count);
        }
        widget->core.window =
            XCreateWindow(XtDisplay(widget),
                          (widget->core.parent ?
                           widget->core.parent->core.window :
                           widget->core.screen->root),
                          (int) widget->core.x, (int) widget->core.y,
                          (unsigned) widget->core.width,
                          (unsigned) widget->core.height,
                          (unsigned) widget->core.border_width,
                          (int) widget->core.depth, window_class, visual,
                          value_mask, attributes);
    }
    UNLOCK_APP(app);
}                               /* XtCreateWindow */

/* ---------------- XtNameToWidget ----------------- */

static Widget NameListToWidget(Widget root,
                               XrmNameList names,
                               XrmBindingList bindings,
                               int in_depth, int *out_depth, int *found_depth);

typedef Widget(*NameMatchProc) (XrmNameList,
                                XrmBindingList,
                                WidgetList, Cardinal, int, int *, int *);

static Widget
MatchExactChildren(XrmNameList names,
                   XrmBindingList bindings,
                   register WidgetList children,
                   register Cardinal num,
                   int in_depth,
                   int *out_depth,
                   int *found_depth)
{
    register Cardinal i;
    register XrmName name = *names;
    Widget w, result = NULL;
    int d, min = 10000;

    for (i = 0; i < num; i++) {
        if (name == children[i]->core.xrm_name) {
            w = NameListToWidget(children[i], &names[1], &bindings[1],
                                 in_depth + 1, &d, found_depth);
            if (w != NULL && d < min) {
                result = w;
                min = d;
            }
        }
    }
    *out_depth = min;
    return result;
}

static Widget
MatchWildChildren(XrmNameList names,
                  XrmBindingList bindings,
                  register WidgetList children,
                  register Cardinal num,
                  int in_depth,
                  int *out_depth,
                  int *found_depth)
{
    register Cardinal i;
    Widget w, result = NULL;
    int d, min = 10000;

    for (i = 0; i < num; i++) {
        w = NameListToWidget(children[i], names, bindings,
                             in_depth + 1, &d, found_depth);
        if (w != NULL && d < min) {
            result = w;
            min = d;
        }
    }
    *out_depth = min;
    return result;
}

static Widget
SearchChildren(Widget root,
               XrmNameList names,
               XrmBindingList bindings,
               NameMatchProc matchproc,
               int in_depth,
               int *out_depth,
               int *found_depth)
{
    Widget w1 = NULL, w2;
    int d1, d2;

    if (XtIsComposite(root)) {
        w1 = (*matchproc) (names, bindings,
                           ((CompositeWidget) root)->composite.children,
                           ((CompositeWidget) root)->composite.num_children,
                           in_depth, &d1, found_depth);
    }
    else
        d1 = 10000;
    w2 = (*matchproc) (names, bindings, root->core.popup_list,
                       root->core.num_popups, in_depth, &d2, found_depth);
    *out_depth = (d1 < d2 ? d1 : d2);
    return (d1 < d2 ? w1 : w2);
}

static Widget
NameListToWidget(register Widget root,
                 XrmNameList names,
                 XrmBindingList bindings,
                 int in_depth,
                 int *out_depth,
                 int *found_depth)
{
    int d1, d2;

    if (in_depth >= *found_depth) {
        *out_depth = 10000;
        return NULL;
    }

    if (names[0] == NULLQUARK) {
        *out_depth = *found_depth = in_depth;
        return root;
    }

    if (!XtIsWidget(root)) {
        *out_depth = 10000;
        return NULL;
    }

    if (*bindings == XrmBindTightly) {
        return SearchChildren(root, names, bindings, MatchExactChildren,
                              in_depth, out_depth, found_depth);

    }
    else {                      /* XrmBindLoosely */
        Widget w1, w2;

        w1 = SearchChildren(root, names, bindings, MatchExactChildren,
                            in_depth, &d1, found_depth);
        w2 = SearchChildren(root, names, bindings, MatchWildChildren,
                            in_depth, &d2, found_depth);
        *out_depth = (d1 < d2 ? d1 : d2);
        return (d1 < d2 ? w1 : w2);
    }
}                               /* NameListToWidget */

Widget
XtNameToWidget(Widget root, _Xconst char *name)
{
    XrmName *names;
    XrmBinding *bindings;
    int len, depth, found = 10000;
    Widget result;

    WIDGET_TO_APPCON(root);

    len = (int) strlen(name);
    if (len == 0)
        return NULL;

    LOCK_APP(app);
    names = (XrmName *) ALLOCATE_LOCAL((unsigned) (len + 1) * sizeof(XrmName));
    bindings = (XrmBinding *)
        ALLOCATE_LOCAL((unsigned) (len + 1) * sizeof(XrmBinding));
    if (names == NULL || bindings == NULL)
        _XtAllocError(NULL);

    XrmStringToBindingQuarkList(name, bindings, names);
    if (names[0] == NULLQUARK) {
        DEALLOCATE_LOCAL((char *) bindings);
        DEALLOCATE_LOCAL((char *) names);
        UNLOCK_APP(app);
        return NULL;
    }

    result = NameListToWidget(root, names, bindings, 0, &depth, &found);

    DEALLOCATE_LOCAL((char *) bindings);
    DEALLOCATE_LOCAL((char *) names);
    UNLOCK_APP(app);
    return result;
}                               /* XtNameToWidget */

/* Define user versions of intrinsics macros */

#undef XtDisplayOfObject
Display *
XtDisplayOfObject(Widget object)
{
    /* Attempts to LockApp() here will generate endless recursive loops */
    if (XtIsSubclass(object, hookObjectClass))
        return DisplayOfScreen(((HookObject) object)->hooks.screen);
    return XtDisplay(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
}

#undef XtDisplay
Display *
XtDisplay(Widget widget)
{
    /* Attempts to LockApp() here will generate endless recursive loops */
    return DisplayOfScreen(widget->core.screen);
}

#undef XtScreenOfObject
Screen *
XtScreenOfObject(Widget object)
{
    /* Attempts to LockApp() here will generate endless recursive loops */
    if (XtIsSubclass(object, hookObjectClass))
        return ((HookObject) object)->hooks.screen;
    return XtScreen(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
}

#undef XtScreen
Screen *
XtScreen(Widget widget)
{
    /* Attempts to LockApp() here will generate endless recursive loops */
    return widget->core.screen;
}

#undef XtWindowOfObject
Window
XtWindowOfObject(Widget object)
{
    return XtWindow(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
}

#undef XtWindow
Window
XtWindow(Widget widget)
{
    return widget->core.window;
}

#undef XtSuperclass
WidgetClass
XtSuperclass(Widget widget)
{
    WidgetClass retval;

    LOCK_PROCESS;
    retval = XtClass(widget)->core_class.superclass;
    UNLOCK_PROCESS;
    return retval;
}

#undef XtClass
WidgetClass
XtClass(Widget widget)
{
    WidgetClass retval;

    LOCK_PROCESS;
    retval = widget->core.widget_class;
    UNLOCK_PROCESS;
    return retval;
}

#undef XtIsManaged
Boolean
XtIsManaged(Widget object)
{
    Boolean retval;

    WIDGET_TO_APPCON(object);

    LOCK_APP(app);
    if (XtIsRectObj(object))
        retval = object->core.managed;
    else
        retval = False;
    UNLOCK_APP(app);
    return retval;
}

#undef XtIsRealized
Boolean
XtIsRealized(Widget object)
{
    Boolean retval;

    WIDGET_TO_APPCON(object);

    LOCK_APP(app);
    retval = XtWindowOfObject(object) != None;
    UNLOCK_APP(app);
    return retval;
}                               /* XtIsRealized */

#undef XtIsSensitive
Boolean
XtIsSensitive(Widget object)
{
    Boolean retval;

    WIDGET_TO_APPCON(object);

    LOCK_APP(app);
    if (XtIsRectObj(object))
        retval = object->core.sensitive && object->core.ancestor_sensitive;
    else
        retval = False;
    UNLOCK_APP(app);
    return retval;
}

/*
 * Internal routine; must be called only after XtIsWidget returns false
 */
Widget
_XtWindowedAncestor(register Widget object)
{
    Widget obj = object;

    for (object = XtParent(object); object && !XtIsWidget(object);)
        object = XtParent(object);

    if (object == NULL) {
        String params = XtName(obj);
        Cardinal num_params = 1;

        XtErrorMsg("noWidgetAncestor", "windowedAncestor", XtCXtToolkitError,
                   "Object \"%s\" does not have windowed ancestor",
                   &params, &num_params);
    }

    return object;
}

#undef XtParent
Widget
XtParent(Widget widget)
{
    /* Attempts to LockApp() here will generate endless recursive loops */
    return widget->core.parent;
}

#undef XtName
String
XtName(Widget object)
{
    /* Attempts to LockApp() here will generate endless recursive loops */
    return XrmQuarkToString(object->core.xrm_name);
}

Boolean
XtIsObject(Widget object)
{
    WidgetClass wc;
    String class_name;

    /* perform basic sanity checks */
    if (object->core.self != object || object->core.xrm_name == NULLQUARK)
        return False;

    LOCK_PROCESS;
    wc = object->core.widget_class;
    if (wc->core_class.class_name == NULL ||
        wc->core_class.xrm_class == NULLQUARK ||
        (class_name = XrmClassToString(wc->core_class.xrm_class)) == NULL ||
        strcmp(wc->core_class.class_name, class_name) != 0) {
        UNLOCK_PROCESS;
        return False;
    }
    UNLOCK_PROCESS;

    if (XtIsWidget(object)) {
        if (object->core.name == NULL ||
            (class_name = XrmNameToString(object->core.xrm_name)) == NULL ||
            strcmp(object->core.name, class_name) != 0)
            return False;
    }
    return True;
}

#if defined(WIN32)
static int
access_file(char *path, char *pathbuf, int len_pathbuf, char **pathret)
{
    if (access(path, F_OK) == 0) {
        if (strlen(path) < len_pathbuf)
            *pathret = pathbuf;
        else
            *pathret = XtMalloc(strlen(path));
        if (*pathret) {
            strcpy(*pathret, path);
            return 1;
        }
    }
    return 0;
}

static int
AccessFile(char *path, char *pathbuf, int len_pathbuf, char **pathret)
{
    unsigned long drives;
    int i, len;
    char *drive;
    char buf[MAX_PATH];
    char *bufp;

    /* just try the "raw" name first and see if it works */
    if (access_file(path, pathbuf, len_pathbuf, pathret))
        return 1;

#if defined(WIN32) && defined(__MINGW32__)
    /* don't try others */
    return 0;
#endif

    /* try the places set in the environment */
    drive = getenv("_XBASEDRIVE");
    if (!drive)
        drive = "C:";
    len = strlen(drive) + strlen(path);
    bufp = XtStackAlloc(len + 1, buf);
    strcpy(bufp, drive);
    strcat(bufp, path);
    if (access_file(bufp, pathbuf, len_pathbuf, pathret)) {
        XtStackFree(bufp, buf);
        return 1;
    }

    /* one last place to look */
    drive = getenv("HOMEDRIVE");
    if (drive) {
        len = strlen(drive) + strlen(path);
        bufp = XtStackAlloc(len + 1, buf);
        strcpy(bufp, drive);
        strcat(bufp, path);
        if (access_file(bufp, pathbuf, len_pathbuf, pathret)) {
            XtStackFree(bufp, buf);
            return 1;
        }
    }

    /* does OS/2 (with or with gcc-emx) have getdrives()? */
    /* tried everywhere else, go fishing */
    drives = _getdrives();
#define C_DRIVE ('C' - 'A')
#define Z_DRIVE ('Z' - 'A')
    for (i = C_DRIVE; i <= Z_DRIVE; i++) {      /* don't check on A: or B: */
        if ((1 << i) & drives) {
            len = 2 + strlen(path);
            bufp = XtStackAlloc(len + 1, buf);
            *bufp = 'A' + i;
            *(bufp + 1) = ':';
            *(bufp + 2) = '\0';
            strcat(bufp, path);
            if (access_file(bufp, pathbuf, len_pathbuf, pathret)) {
                XtStackFree(bufp, buf);
                return 1;
            }
        }
    }
    return 0;
}
#endif

static Boolean
TestFile(String path)
{
#ifndef VMS
    int ret = 0;
    struct stat status;

#if defined(WIN32)
    char buf[MAX_PATH];
    char *bufp;
    int len;
    UINT olderror = SetErrorMode(SEM_FAILCRITICALERRORS);

    if (AccessFile(path, buf, MAX_PATH, &bufp))
        path = bufp;

    (void) SetErrorMode(olderror);
#endif
    ret = (access(path, R_OK) == 0 &&   /* exists and is readable */
           stat(path, &status) == 0 &&  /* get the status */
#ifndef X_NOT_POSIX
           S_ISDIR(status.st_mode) == 0);       /* not a directory */
#else
           (status.st_mode & S_IFMT) != S_IFDIR);       /* not a directory */
#endif                          /* X_NOT_POSIX else */
    return (Boolean) ret;
#else                           /* VMS */
    return TRUE;                /* Who knows what to do here? */
#endif                          /* VMS */
}

/* return of TRUE = resolved string fit, FALSE = didn't fit.  Not
   null-terminated and not collapsed if it didn't fit */

static Boolean Resolve(register _Xconst char *source,   /* The source string */
                       register int len,        /* The length in bytes of *source */
                       Substitution sub,        /* Array of string values to substitute */
                       Cardinal num,    /* Number of substitution entries */
                       char *buf,       /* Where to put the resolved string; */
                       char collapse) { /* Character to collapse */
    register int bytesLeft = PATH_MAX;
    register char *bp = buf;

#ifndef DONT_COLLAPSE
    Boolean atBeginning = TRUE;
    Boolean prevIsCollapse = FALSE;

#define PUT(ch) \
    { \
        if (--bytesLeft == 0) return FALSE; \
        if (prevIsCollapse) \
            if ((*bp = ch) != collapse) { \
                prevIsCollapse = FALSE; \
                bp++; \
            } \
            else bytesLeft++; \
        else if ((*bp++ = ch) == collapse && !atBeginning) \
            prevIsCollapse = TRUE; \
    }
#else                           /* DONT_COLLAPSE */

#define PUT(ch) \
    { \
        if (--bytesLeft == 0) return FALSE; \
        *bp++ = ch; \
    }
#endif                          /* DONT_COLLAPSE */
#define escape '%'

    while (len--) {
#ifndef DONT_COLLAPSE
        if (*source == collapse) {
            PUT(*source);
            source++;
            continue;
        }
        else
#endif                          /* DONT_COLLAPSE */
        if (*source != escape) {
            PUT(*source);
        }
        else {
            source++;
            if (len-- == 0) {
                PUT(escape);
                break;
            }

            if (*source == ':' || *source == escape) {
                PUT(*source);
            }
            else {
                /* Match the character against the match array */
                register Cardinal j;

                for (j = 0; j < num && sub[j].match != *source; j++) {
                }

                /* Substitute the substitution string */

                if (j >= num) {
                    PUT(*source);
                }
                else if (sub[j].substitution != NULL) {
                    char *sp = sub[j].substitution;

                    while (*sp) {
                        PUT(*sp);
                        sp++;
                    }
                }
            }
        }
        source++;
#ifndef DONT_COLLAPSE
        atBeginning = FALSE;
#endif                          /* DONT_COLLAPSE */
    }
    PUT('\0');

    return TRUE;
#undef PUT
#undef escape
}

_XtString
XtFindFile(_Xconst _XtString path,
           Substitution substitutions,
           Cardinal num_substitutions,
           XtFilePredicate predicate)
{
    char *buf, *buf1, *buf2;
    _Xconst _XtString colon;
    int len;
    Boolean firstTime = TRUE;

    buf1 = __XtMalloc((unsigned) PATH_MAX);
    buf2 = __XtMalloc((unsigned) PATH_MAX);
    buf = buf1;

    if (predicate == NULL)
        predicate = TestFile;

    while (1) {
        colon = path;
        /* skip leading colons */
        while (*colon) {
            if (*colon != ':')
                break;
            colon++;
            path++;
        }
        /* now look for an un-escaped colon */
        for (; *colon; colon++) {
            if (*colon == '%' && *(path + 1)) {
                colon++;        /* bump it an extra time to skip %. */
                continue;
            }
            if (*colon == ':')
                break;
        }
        len = (int) (colon - path);
        if (Resolve(path, len, substitutions, num_substitutions, buf, '/')) {
            if (firstTime || strcmp(buf1, buf2) != 0) {
#ifdef XNL_DEBUG
                printf("Testing file %s\n", buf);
#endif                          /* XNL_DEBUG */
                /* Check out the file */
                if ((*predicate) (buf)) {
                    /* We've found it, return it */
#ifdef XNL_DEBUG
                    printf("File found.\n");
#endif                          /* XNL_DEBUG */
                    if (buf == buf1) {
                        XtFree(buf2);
                        return buf1;
                    }
                    XtFree(buf1);
                    return buf2;
                }
                if (buf == buf1)
                    buf = buf2;
                else
                    buf = buf1;
                firstTime = FALSE;
            }
        }

        /* Nope...any more paths? */

        if (*colon == '\0')
            break;
        path = colon + 1;
    }

    /* No file found */

    XtFree(buf1);
    XtFree(buf2);
    return NULL;
}

/* The implementation of this routine is operating system dependent */
/* Should match the code in Xlib _XlcMapOSLocaleName */

static String
ExtractLocaleName(String lang)
{

#if defined(hpux) || defined(CSRG_BASED) || defined(sun) || defined(SVR4) || defined(sgi) || defined(__osf__) || defined(AIXV3) || defined(ultrix) || defined(WIN32) || defined (linux)
#ifdef hpux
/*
 * We need to discriminated between HPUX 9 and HPUX 10. The equivalent
 * code in Xlib in SetLocale.c does include locale.h via X11/Xlocale.h.
 */
#include <locale.h>
#ifndef _LastCategory
    /* HPUX 9 and earlier */
#define SKIPCOUNT 2
#define STARTCHAR ':'
#define ENDCHAR ';'
#else
    /* HPUX 10 */
#define ENDCHAR ' '
#endif
#else
#ifdef ultrix
#define SKIPCOUNT 2
#define STARTCHAR '\001'
#define ENDCHAR '\001'
#else
#ifdef WIN32
#define SKIPCOUNT 1
#define STARTCHAR '='
#define ENDCHAR ';'
#define WHITEFILL
#else
#if defined(__osf__) || (defined(AIXV3) && !defined(AIXV4))
#define STARTCHAR ' '
#define ENDCHAR ' '
#else
#if defined(linux)
#define STARTSTR "LC_CTYPE="
#define ENDCHAR ';'
#else
#if !defined(sun) || defined(SVR4)
#define STARTCHAR '/'
#define ENDCHAR '/'
#endif
#endif
#endif
#endif
#endif
#endif

    String start;
    String end;
    int len;

#ifdef SKIPCOUNT
    int n;
#endif
    static char *buf = NULL;

#ifdef WHITEFILL
    char *temp;
#endif

    start = lang;
#ifdef SKIPCOUNT
    for (n = SKIPCOUNT;
         --n >= 0 && start && (start = strchr(start, STARTCHAR)); start++);
    if (!start)
        start = lang;
#endif
#ifdef STARTCHAR
    if (start && (start = strchr(start, STARTCHAR)))
#elif  defined (STARTSTR)
    if (start && (start = strstr(start, STARTSTR)))
#endif
    {
#ifdef STARTCHAR
        start++;
#elif defined (STARTSTR)
        start += strlen(STARTSTR);
#endif

        if ((end = strchr(start, ENDCHAR))) {
            len = (int) (end - start);
            XtFree(buf);
            buf = XtMalloc((Cardinal) (len + 1));
            if (buf == NULL)
                return NULL;
            strncpy(buf, start, (size_t) len);
            *(buf + len) = '\0';
#ifdef WHITEFILL
            for (temp = buf; (temp = strchr(temp, ' ')) != NULL;)
                *temp++ = '-';
#endif
            return buf;
        }
        else                    /* if no ENDCHAR is found we are at the end of the line */
            return start;
    }
#ifdef WHITEFILL
    if (strchr(lang, ' ')) {
        XtFree(buf);
        buf = strdup(lang);
        if (buf == NULL)
            return NULL;
        for (temp = buf; (temp = strchr(temp, ' ')) != NULL;)
            *temp++ = '-';
        return buf;
    }
#endif
#undef STARTCHAR
#undef ENDCHAR
#undef WHITEFILL
#endif

    return lang;
}

static void
FillInLangSubs(Substitution subs, XtPerDisplay pd)
{
    int len;
    String string;
    char *p1, *p2, *p3;
    char **rest;
    char *ch;

    if (pd->language == NULL ||
        (pd->language != NULL && pd->language[0] == '\0')) {
        subs[0].substitution = subs[1].substitution =
            subs[2].substitution = subs[3].substitution = NULL;
        return;
    }

    string = ExtractLocaleName(pd->language);

    if (string == NULL || (string != NULL && string[0] == '\0')) {
        subs[0].substitution = subs[1].substitution =
            subs[2].substitution = subs[3].substitution = NULL;
        return;
    }

    len = (int) strlen(string) + 1;
    subs[0].substitution = (_XtString) string;
    p1 = subs[1].substitution = __XtMalloc((Cardinal) (3 * len));
    p2 = subs[2].substitution = subs[1].substitution + len;
    p3 = subs[3].substitution = subs[2].substitution + len;

    /* Everything up to the first "_" goes into p1.  From "_" to "." in
       p2.  The rest in p3.  If no delimiters, all goes into p1.  We
       assume p1, p2, and p3 are large enough. */

    *p1 = *p2 = *p3 = '\0';

    ch = strchr(string, '_');
    if (ch != NULL) {
        len = (int) (ch - string);
        (void) strncpy(p1, string, (size_t) len);
        p1[len] = '\0';
        string = ch + 1;
        rest = &p2;
    }
    else
        rest = &p1;

    /* Rest points to where we put the first part */

    ch = strchr(string, '.');
    if (ch != NULL) {
        len = (int) (ch - string);
        strncpy(*rest, string, (size_t) len);
        (*rest)[len] = '\0';
        (void) strcpy(p3, ch + 1);
    }
    else
        (void) strcpy(*rest, string);
}

/*
 * default path used if environment variable XFILESEARCHPATH
 * is not defined.  Also substitued for %D.
 * The exact value should be documented in the implementation
 * notes for any Xt implementation.
 */
static const char *
implementation_default_path(void)
{
#if defined(WIN32)
    static char xfilesearchpath[] = "";

    return xfilesearchpath;
#else
    return XFILESEARCHPATHDEFAULT;
#endif
}


/* *INDENT-OFF* */
static SubstitutionRec defaultSubs[] = {
    {'N', NULL},
    {'T', NULL},
    {'S', NULL},
    {'C', NULL},
    {'L', NULL},
    {'l', NULL},
    {'t', NULL},
    {'c', NULL}
};
/* *INDENT-ON* */

_XtString
XtResolvePathname(Display *dpy,
                  _Xconst char *type,
                  _Xconst char *filename,
                  _Xconst char *suffix,
                  _Xconst char *path,
                  Substitution substitutions,
                  Cardinal num_substitutions,
                  XtFilePredicate predicate)
{
    XtPerDisplay pd;
    static const char *defaultPath = NULL;
    const char *impl_default = implementation_default_path();
    int idef_len = (int) strlen(impl_default);
    char *massagedPath;
    int bytesAllocd, bytesLeft;
    char *ch, *result;
    Substitution merged_substitutions;
    XrmRepresentation db_type;
    XrmValue value;
    XrmName name_list[3];
    XrmClass class_list[3];
    Boolean pathMallocd = False;

    LOCK_PROCESS;
    pd = _XtGetPerDisplay(dpy);
    if (path == NULL) {
#ifndef VMS
        if (defaultPath == NULL) {
            defaultPath = getenv("XFILESEARCHPATH");
            if (defaultPath == NULL)
                defaultPath = impl_default;
        }
        path = defaultPath;
#endif                          /* VMS */
    }

    if (path == NULL)
        path = "";              /* NULL would kill us later */

    if (filename == NULL) {
        filename = XrmClassToString(pd->class);
    }

    bytesAllocd = bytesLeft = 1000;
    massagedPath = ALLOCATE_LOCAL((size_t) bytesAllocd);
    if (massagedPath == NULL)
        _XtAllocError(NULL);

    if (path[0] == ':') {
        strcpy(massagedPath, "%N%S");
        ch = &massagedPath[4];
        bytesLeft -= 4;
    }
    else
        ch = massagedPath;

    /* Insert %N%S between adjacent colons
     * and default path for %D.
     * Default path should not have any adjacent colons of its own.
     */

    while (*path != '\0') {
        if (bytesLeft < idef_len) {
            int bytesUsed = bytesAllocd - bytesLeft;
            char *new;

            bytesAllocd += 1000;
            new = __XtMalloc((Cardinal) bytesAllocd);
            strncpy(new, massagedPath, (size_t) bytesUsed);
            ch = new + bytesUsed;
            if (pathMallocd)
                XtFree(massagedPath);
            else
                DEALLOCATE_LOCAL(massagedPath);
            pathMallocd = True;
            massagedPath = new;
            bytesLeft = bytesAllocd - bytesUsed;
        }
        if (*path == '%' && *(path + 1) == ':') {
            *ch++ = '%';
            *ch++ = ':';
            path += 2;
            bytesLeft -= 2;
            continue;
        }
        if (*path == ':' && *(path + 1) == ':') {
            strcpy(ch, ":%N%S:");
            ch += 6;
            bytesLeft -= 6;
            while (*path == ':')
                path++;
            continue;
        }
        if (*path == '%' && *(path + 1) == 'D') {
            strcpy(ch, impl_default);
            ch += idef_len;
            bytesLeft -= idef_len;
            path += 2;
            continue;
        }
        *ch++ = *path++;
        bytesLeft--;
    }
    *ch = '\0';
#ifdef XNL_DEBUG
    printf("Massaged path: %s\n", massagedPath);
#endif                          /* XNL_DEBUG */

    if (num_substitutions == 0)
        merged_substitutions = defaultSubs;
    else {
        int i = XtNumber(defaultSubs);
        Substitution sub, def;

        merged_substitutions = sub = (Substitution)
            ALLOCATE_LOCAL((unsigned) (num_substitutions + (Cardinal) i) *
                           sizeof(SubstitutionRec));
        if (sub == NULL)
            _XtAllocError(NULL);
        for (def = defaultSubs; i--; sub++, def++)
            sub->match = def->match;
        for (i = (int) num_substitutions; i--;)
            *sub++ = *substitutions++;
    }
    merged_substitutions[0].substitution = (_XtString) filename;
    merged_substitutions[1].substitution = (_XtString) type;
    merged_substitutions[2].substitution = (_XtString) suffix;
    name_list[0] = pd->name;
    name_list[1] = XrmPermStringToQuark("customization");
    name_list[2] = NULLQUARK;
    class_list[0] = pd->class;
    class_list[1] = XrmPermStringToQuark("Customization");
    class_list[2] = NULLQUARK;
    if (XrmQGetResource(XrmGetDatabase(dpy), name_list, class_list,
                        &db_type, &value) && db_type == _XtQString)
        merged_substitutions[3].substitution = (char *) value.addr;
    else
        merged_substitutions[3].substitution = NULL;
    FillInLangSubs(&merged_substitutions[4], pd);

    result = XtFindFile(massagedPath, merged_substitutions,
                        num_substitutions + XtNumber(defaultSubs), predicate);

    if (merged_substitutions[5].substitution != NULL)
        XtFree((XtPointer) merged_substitutions[5].substitution);

    if (merged_substitutions != defaultSubs)
        DEALLOCATE_LOCAL(merged_substitutions);

    if (pathMallocd)
        XtFree(massagedPath);
    else
        DEALLOCATE_LOCAL(massagedPath);

    UNLOCK_PROCESS;
    return result;
}

Boolean
XtCallAcceptFocus(Widget widget, Time *time)
{
    XtAcceptFocusProc ac;
    Boolean retval;

    WIDGET_TO_APPCON(widget);

    LOCK_APP(app);
    LOCK_PROCESS;
    ac = XtClass(widget)->core_class.accept_focus;
    UNLOCK_PROCESS;

    if (ac != NULL)
        retval = (*ac) (widget, time);
    else
        retval = FALSE;
    UNLOCK_APP(app);
    return retval;
}

#ifdef XT_GEO_TATTLER
/**************************************************************************
 GeoTattler:  This is used to debug Geometry management in Xt.

  It uses a pseudo resource XtNgeotattler.

  E.G. if those lines are found in the resource database:

    myapp*draw.XmScale.geoTattler: ON
    *XmScrollBar.geoTattler:ON
    *XmRowColumn.exit_button.geoTattler:ON

   then:

    all the XmScale children of the widget named draw,
    all the XmScrollBars,
    the widget named exit_button in any XmRowColumn

   will return True to the function IsTattled(), and will generate
   outlined trace to stdout.

*************************************************************************/

#define XtNgeoTattler "geoTattler"
#define XtCGeoTattler "GeoTattler"

typedef struct {
    Boolean geo_tattler;
} GeoDataRec;

/* *INDENT-OFF* */
static XtResource geo_resources[] = {
    { XtNgeoTattler, XtCGeoTattler, XtRBoolean, sizeof(Boolean),
      XtOffsetOf(GeoDataRec, geo_tattler),
      XtRImmediate, (XtPointer) False }
};
/* *INDENT-ON* */

/************************************************************************
  This function uses XtGetSubresources to find out if a widget
  needs to be geo-spied by the caller. */
static Boolean
IsTattled(Widget widget)
{
    GeoDataRec geo_data;

    XtGetSubresources(widget, (XtPointer) &geo_data,
                      (String) NULL, (String) NULL,
                      geo_resources, XtNumber(geo_resources), NULL, 0);

    return geo_data.geo_tattler;

}                               /* IsTattled */

static int n_tab = 0;           /* not MT for now */

void
_XtGeoTab(int direction)
{                               /* +1 or -1 */
    n_tab += direction;
}

void
_XtGeoTrace(Widget widget, const char *fmt, ...)
{
    if (IsTattled(widget)) {
        va_list args;
        int i;

        va_start(args, fmt);
        for (i = 0; i < n_tab; i++)
            printf("     ");
        (void) vprintf(fmt, args);
        va_end(args);
    }
}

#endif                          /* XT_GEO_TATTLER */
