/* $XConsortium: geoIntrinsic.c /main/4 1995/07/15 21:13:22 drk $ */
/*
 *  @OPENGROUP_COPYRIGHT@
 *  COPYRIGHT NOTICE
 *  Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc.
 *  Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group
 *  ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for
 *  the full copyright text.
 *  
 *  This software is subject to an open license. It may only be
 *  used on, with or for operating systems which are themselves open
 *  source systems. You must contact The Open Group for a license
 *  allowing distribution and sublicensing of this software on, with,
 *  or for operating systems which are not Open Source programs.
 *  
 *  See http://www.opengroup.org/openmotif/license for full
 *  details of the license agreement. Any use, reproduction, or
 *  distribution of the program constitutes recipient's acceptance of
 *  this agreement.
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
 *  PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 *  WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
 *  OR FITNESS FOR A PARTICULAR PURPOSE
 *  
 *  EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
 *  NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 *  DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 *  ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
 *  EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGES.
 */
/*
 * HISTORY
 */

#define INTRINSIC_C

#include "IntrinsicI.h"
#include "StringDefs.h"
#ifndef NO_IDENTIFY_WINDOWS
#include <X11/Xatom.h>
#endif
#ifndef VMS
#include <sys/stat.h>
#endif /* VMS */

#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#else
extern char *getenv();
#endif

#include "geoTattler.h"

String XtCXtToolkitError = "XtToolkitError";

Boolean XtIsSubclass(widget, widgetClass)
    Widget    widget;
    WidgetClass widgetClass;
{
    register WidgetClass w;

    for (w = widget->core.widget_class; w != NULL; w = w->core_class.superclass)
	if (w == widgetClass) return (TRUE);
    return (FALSE);
} /* XtIsSubclass */


#if NeedFunctionPrototypes
Boolean _XtCheckSubclassFlag(
    Widget object,
    _XtXtEnum flag
    )
#else
Boolean _XtCheckSubclassFlag(object, flag)
    Widget object;
    XtEnum flag;
#endif
{
    if (object->core.widget_class->core_class.class_inited & flag)
	return True;
    else
	return False;

} /*_XtVerifySubclass */


#if NeedFunctionPrototypes
Boolean _XtIsSubclassOf(
    Widget object,
    WidgetClass widgetClass,
    WidgetClass superClass,
    _XtXtEnum flag
    )
#else
Boolean _XtIsSubclassOf(object, widgetClass, superClass, flag)
    Widget object;
    WidgetClass widgetClass, superClass;
    XtEnum flag;
#endif
{
    if (!(object->core.widget_class->core_class.class_inited & flag))
	return False;
    else {
	register WidgetClass c = object->core.widget_class;
	while (c != superClass) {
	    if (c == widgetClass)
		return True;
	    c = c->core_class.superclass;
	}
	return False;
    }
} /*_XtIsSubclassOf */


static void ComputeWindowAttributes(widget,value_mask,values)
    Widget		 widget;
    XtValueMask		 *value_mask;
    XSetWindowAttributes *values;
{
    *value_mask = CWEventMask | CWColormap;
    (*values).event_mask = 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;
    }
    if (widget->core.widget_class->core_class.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(widget)
    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;
    change_managed = clPtr->change_managed;

    /* 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) {
	_GeoPrintTrace(widget,"Call \"%s\"[%d,%d]'s changemanaged\n", XtName(widget), widget->core.width, widget->core.height);
	(*change_managed) (widget);
    }
} /* CallChangeManaged */


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

    children = cwp->children;
    for (i = 0; i <  cwp->num_children; i++) {
	child = children[i];
	if (XtIsWidget (child)){
	    if (child->core.managed && child->core.mapped_when_managed) {
		XtMapWidget (children[i]);
	    }
	}
    }
} /* MapChildren */


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

    children = cwp->children;
    for (i = 0; i < cwp->num_children; i++) {
	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)
    register Widget		widget;
{
    XtValueMask			value_mask;
    XSetWindowAttributes	values;
    XtRealizeProc		realize;
    Window			window;

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

    _XtInstallTranslations(widget);

    ComputeWindowAttributes (widget, &value_mask, &values);
    realize = widget->core.widget_class->core_class.realize;
    if (realize == NULL)
	XtAppErrorMsg(XtWidgetToApplicationContext(widget),
		      "invalidProcedure","realizeProc",XtCXtToolkitError,
		      "No realize class procedure defined",
		      (String *)NULL, (Cardinal *)NULL);
    else {
	_GeoPrintTrace(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);
#ifndef NO_IDENTIFY_WINDOWS
    if (_XtGetPerDisplay(XtDisplay(widget))->appContext->identify_windows) {
	int len_nm, len_cl;
 	char *s;

	len_nm = widget->core.name ? strlen(widget->core.name) : 0;
 	len_cl = strlen(widget->core.widget_class->core_class.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,
 	       widget->core.widget_class->core_class.class_name);
 	XChangeProperty(XtDisplay(widget), window,
 			XInternAtom(XtDisplay(widget), "_MIT_OBJ_CLASS",
 				    False),
 			XA_STRING, 8, PropModeReplace, (unsigned char *) s, 
 			len_nm + len_cl + 2);
 	XtFree(s);
    }
#endif
#ifdef notdef
    _XtRegisterAsyncHandlers(widget);
#endif
    _XtRegisterGrabs(widget);
    _XtRegisterWindow (window, widget);

    if (XtIsComposite (widget)) {
	register Cardinal		i;
	register CompositePart *cwp = &(((CompositeWidget)widget)->composite);
	register 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 (XtDisplay(widget), 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)
    register Widget		widget;
{
    if (XtIsRealized (widget)) return;

    CallChangeManaged(widget);
    RealizeWidget(widget);
} /* XtRealizeWidget */


static void UnrealizeWidget(widget)
    register Widget		widget;
{
    register CompositeWidget	cw;
    register Cardinal		i;
    register WidgetList		children;

    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)) {
	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 */
    _XtUnregisterWindow(XtWindow(widget), 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)
    register Widget		widget;
{
    Window window = XtWindow(widget);

    if (! XtIsRealized (widget)) return;

    if (widget->core.parent != NULL) XtUnmanageChild(widget);

    UnrealizeWidget(widget);

    if (window != None) XDestroyWindow(XtDisplay(widget), window);
} /* XtUnrealizeWidget */


void XtCreateWindow(widget, window_class, visual, value_mask, attributes)
    Widget		 widget;
    unsigned int	 window_class;
    Visual		 *visual;
    XtValueMask		 value_mask;
    XSetWindowAttributes *attributes;
{
    if (widget->core.window == None) {
	if (widget->core.width == 0 || widget->core.height == 0) {
	    Cardinal count = 1;
	    XtAppErrorMsg(XtWidgetToApplicationContext(widget),
		       "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);
    }
} /* XtCreateWindow */


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

static Widget NameListToWidget();

typedef Widget (*NameMatchProc)();

static Widget MatchExactChildren(names, bindings, children, num,
	in_depth, out_depth, found_depth)
    XrmNameList     names;
    XrmBindingList  bindings;
    register WidgetList children;
    register int num;
    int in_depth, *out_depth, *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(names, bindings, children, num,
	in_depth, out_depth, found_depth)
    XrmNameList     names;
    XrmBindingList  bindings;
    register WidgetList children;
    register int num;
    int in_depth, *out_depth, *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(root, names, bindings, matchproc,
	in_depth, out_depth, found_depth)
    Widget root;
    XrmNameList     names;
    XrmBindingList  bindings;
    NameMatchProc matchproc;
    int in_depth, *out_depth, *found_depth;
{
    Widget w1, 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(root, names, bindings,
	in_depth, out_depth, found_depth)
    register Widget root;
    XrmNameList     names;
    XrmBindingList  bindings;
    int in_depth, *out_depth, *found_depth;
{
    Widget w1, w2;
    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 */
	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 */

#if NeedFunctionPrototypes
Widget XtNameToWidget(
    Widget root,
    _Xconst char* name
    )
#else
Widget XtNameToWidget(root, name)
    Widget root;
    String name;
#endif
{
    XrmName *names;
    XrmBinding *bindings;
    int len, depth, found = 10000;
    Widget result;

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

    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);
	return NULL;
    }

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

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

/* Define user versions of intrinsics macros */

#undef XtDisplayOfObject
Display *XtDisplayOfObject(object)
     Widget object;
{
    return XtDisplay(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
}

#undef XtDisplay
Display *XtDisplay(widget)
	Widget widget;
{
    return DisplayOfScreen(widget->core.screen);
}

#undef XtScreenOfObject
Screen *XtScreenOfObject(object)
     Widget object;
{
    return XtScreen(XtIsWidget(object) ? object : _XtWindowedAncestor(object));
}

#undef XtScreen
Screen *XtScreen(widget)
	Widget widget;
{
    return widget->core.screen;
}

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


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

#undef XtSuperclass
WidgetClass XtSuperclass(widget)
	Widget widget;
{
	return XtClass(widget)->core_class.superclass;
}

#undef XtClass
WidgetClass XtClass(widget)
	Widget widget;
{
	return widget->core.widget_class;
}

#undef XtIsManaged
Boolean XtIsManaged(object)
	Widget object;
{
    if (XtIsRectObj(object))
	return object->core.managed;
    else
	return False;
}

#undef XtIsRealized
Boolean XtIsRealized (object)
	Widget   object;
{
    return XtWindowOfObject(object) != None;
} /* XtIsRealized */

#undef XtIsSensitive
Boolean XtIsSensitive(object)
	Widget	object;
{
    if (XtIsRectObj(object))
	return object->core.sensitive && object->core.ancestor_sensitive;
    else
	return False;
}

/*
 * Internal routine; must be called only after XtIsWidget returns false
 */
Widget _XtWindowedAncestor(object)
    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 widget;
{
	return widget->core.parent;
}

#undef XtName
String XtName(object)
     Widget object;
{
    return XrmQuarkToString(object->core.xrm_name);
}


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

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

    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)
	    return False;

    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;
}


static Boolean TestFile(path)
    String path;
{
#ifdef VMS
    return TRUE;	/* Who knows what to do here? */
#else
    struct stat status;

    return (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_IFDIR) == 0);	/* not a directory */
#endif /* X_NOT_POSIX else */
#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(source, len, sub, num, buf, collapse)
    register 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 int 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
}


#if NeedFunctionPrototypes
String XtFindFile(
    _Xconst char* path,
    Substitution substitutions,
    Cardinal num_substitutions,
    XtFilePredicate predicate
    )
#else
String XtFindFile(path, substitutions, num_substitutions, predicate)
    String path;
    Substitution substitutions;
    Cardinal num_substitutions;
    XtFilePredicate predicate;
#endif
{
    char *buf, *buf1, *buf2, *colon, *start;
    int len;
    Boolean firstTime = TRUE;

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

    if (predicate == NULL) predicate = TestFile;

    while (1) {
	start = (String)path;
	while (1) {
	    colon = index(start, ':');
	    if (colon == NULL) break;
	    if (colon == path) {start++; path++; continue; }
	    if (*(colon-1) != '%') break;
	    start = colon+1;
	}
	if (colon != NULL)
	    len = colon - path;
	else
	    len = strlen(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);
		    else XtFree(buf1);
		    return buf;
		}
		if (buf == buf1)
		    buf = buf2;
		else
		    buf = buf1;
		firstTime = FALSE;
	    }
	}

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

	if (colon == NULL) break;
	path = colon+1;
    }

    /* No file found */

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


/* The implementation of this routine is operating system dependent */

static char *ExtractLocaleName(lang)
    String	lang;
{

#ifdef hpux	 /* hpux-specific parsing of the locale string */
#define MAXLOCALE       64      /* buffer size of locale name */

    char           *start;
    char           *end;
    int             len;
    static char     buf[MAXLOCALE];

    /*  If lang has a substring ":<category>;", extract <category>
     *  from the first such occurrence as the locale name.
     */

    start = lang;
    if (start = strchr (lang, ':')) {
        start++;
        if (end = strchr (start, ';')) {
            len = end - start;
            strncpy(buf, start, len);
            *(buf + len) = '\0';
            lang = buf;
      }
    }
#endif	/* hpux */

    /*  If result is "C", return NULL instead. */

    if (strcmp(lang, "C"))
        return lang;
    else
      return NULL;
}

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

    if (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[0] == '\0') {
	subs[0].substitution = subs[1].substitution =
		subs[2].substitution = subs[3].substitution = NULL;
	return;
    }

    len = strlen(string) + 1;
    subs[0].substitution = 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 = index(string, '_');
    if (ch != NULL) {
	len = ch - string;
	(void) strncpy(p1, string, len);
	p1[len] = '\0';
	string = ch + 1;
	rest = &p2;
    } else rest = &p1;

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

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

static SubstitutionRec defaultSubs[] = {
    {'N', NULL},
    {'T', NULL},
    {'S', NULL},
    {'C', NULL},
    {'L', NULL},
    {'l', NULL}, 
    {'t', NULL},
    {'c', NULL}
};


#if NeedFunctionPrototypes
String XtResolvePathname(
    Display *dpy,
    _Xconst char* type,
    _Xconst char* filename,
    _Xconst char* suffix,
    _Xconst char* path,
    Substitution substitutions,
    Cardinal num_substitutions,
    XtFilePredicate predicate
    )
#else
String XtResolvePathname(dpy, type, filename, suffix, path, substitutions,
			 num_substitutions, predicate)
    Display *dpy;
    String type, filename, suffix, path;
    Substitution substitutions;
    Cardinal num_substitutions;
    XtFilePredicate predicate;
#endif
{
    XtPerDisplay pd = _XtGetPerDisplay(dpy);
    static char *defaultPath = NULL;
    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;

    if (path == NULL) {
#ifndef VMS
	if (defaultPath == NULL) {
	    defaultPath = getenv("XFILESEARCHPATH");
	    if (defaultPath == NULL) defaultPath = XFILESEARCHPATHDEFAULT;
	}
	path = defaultPath;
#else
	path = "";	/* NULL would kill us later */
#endif /* VMS */
    }	

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

    bytesAllocd = bytesLeft = 1000;
    massagedPath = ALLOCATE_LOCAL(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 */

    while (*path != '\0') {
	if (bytesLeft < 8) {
	    int bytesUsed = bytesAllocd - bytesLeft;
	    char *new;
	    bytesAllocd +=1000;
	    new = XtMalloc((Cardinal) bytesAllocd);
	    strncpy( new, massagedPath, 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;
	}
	*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+i)*sizeof(SubstitutionRec));
	if (sub == NULL) _XtAllocError(NULL);
	for (def = defaultSubs; i--; sub++, def++) sub->match = def->match;
	for (i = num_substitutions; i--; ) *sub++ = *substitutions++;
    }
    merged_substitutions[0].substitution = (String)filename;
    merged_substitutions[1].substitution = (String)type;
    merged_substitutions[2].substitution = (String)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);

    return result;
}


Boolean XtCallAcceptFocus(widget, time)
    Widget widget;
    Time *time;
{
    XtAcceptFocusProc ac = XtClass(widget)->core_class.accept_focus;

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