[comp.sources.x] v11i034: wcl - Widget Creation Library, Part22/35

david@devvax.Jpl.Nasa.Gov (David E. Smyth) (02/09/91)

Submitted-by: david@devvax.Jpl.Nasa.Gov (David E. Smyth)
Posting-number: Volume 11, Issue 34
Archive-name: wcl/part22

#! /bin/sh

# Make a new directory for the wc sources, cd to it, and run kits 1
# thru 35 through sh.  When all 35 kits have been run, read README.

echo "This is wc 1.05 kit 22 (of 35).  If kit 22 is complete, the line"
echo '"'"End of kit 22 (of 35)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir Mri Wc 2>/dev/null
echo Extracting Wc/WcCallb.c
sed >Wc/WcCallb.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X** Copyright (c) 1990 David E. Smyth
X**
X** This file was derived from work performed by Martin Brunecky at
X** Auto-trol Technology Corporation, Denver, Colorado, under the
X** following copyright:
X**
X*******************************************************************************
X* Copyright 1990 by Auto-trol Technology Corporation, Denver, Colorado.
X*
X*                        All Rights Reserved
X*
X* Permission to use, copy, modify, and distribute this software and its
X* documentation for any purpose and without fee is hereby granted, provided
X* that the above copyright notice appears on all copies and that both the
X* copyright and this permission notice appear in supporting documentation
X* and that the name of Auto-trol not be used in advertising or publicity
X* pertaining to distribution of the software without specific, prior written
X* permission.
X*
X* Auto-trol disclaims all warranties with regard to this software, including
X* all implied warranties of merchantability and fitness, in no event shall
X* Auto-trol be liable for any special, indirect or consequential damages or
X* any damages whatsoever resulting from loss of use, data or profits, whether
X* in an action of contract, negligence or other tortious action, arising out
X* of or in connection with the use or performance of this software.
X*******************************************************************************
X**
X** Redistribution and use in source and binary forms are permitted
X** provided that the above copyright notice and this paragraph are
X** duplicated in all such forms and that any documentation, advertising
X** materials, and other materials related to such distribution and use
X** acknowledge that the software was developed by David E. Smyth.  The
X** name of David E. Smyth may not be used to endorse or promote products
X** derived from this software without specific prior written permission.
X** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
X** WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
X** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X**
X*/
X
X/*
X* SCCS_data: @(#)WcCallb.c 1.1 ( 19 Nov 90 )
X*
X* Subsystem_group:
X*
X*     Widget Creation Library
X*
X* Module_description:
X*
X*     This module contains the convenience callbacks used to create and 
X*     manage a widget tree using the Xrm databse.
X*
X*     Several convenience callbacks are provided with the package, allowing 
X*     deferred widget creation, control (manage/unmanage) and other utility
X*     functions.
X*
X* Module_interface_summary: 
X*
X*     Convenience Callbacks:
X*
X* Module_history:
X*                                                  
X*   Several of the callbacks and the callback registration routine were
X*   originally written by Martin Brunecky at Auto-Trol, between about
X*   the first of February 1990 until about 18 April 1990.
X*
X*   Additional callbacks and modifications to all callbacks and the
X*   callback registration routine were made by David Smyth at Jet
X*   Propulsion Laboratories following about 15 March 1990.
X*
X*   WcLoadResourceFileCB enhanced to remember which resource files have
X*   been loaded (by resource file name, not complete path name).  It now
X*   handles absolute pathnames starting at root or tilda, or uses the
X*   same search path algorithm used bu GetAppDefaults in X11R4 Xt.  I.e.,
X*   it uses XUSERFILESEARCHPATH, the user's home directory, and XAPPLRESDIR 
X*   in the same way that XtR4 does when it gets user's application defaults.  
X*   This code basically mimics GetAppUserDefaults() in mit/lib/Xt/Initialize.c
X
X* Design_notes:
X*
X*   For VMS, we could have used LIB$FIND_IMAGE_SYMBOL and use dynamic
X*   (runtime) binding. But since most UNIX systems lack such capability,
X*   we stick to the concept of "registration" routines.
X*
X*   All these callbacks could probably be declared as static.  They
X*   were not because applications may want to link them to widgets
X*   via C code.  
X*
X*   When Motif runs on release 4 Intrinsics, then all argument parsing
X*   should be replaced with coverters, so conversions get cached.  This
X*   will improve performance, especially for pop-ups.
X*
X*******************************************************************************
X*/
X/*
X*******************************************************************************
X* Include_files.
X*******************************************************************************
X*/
X
X#include <X11/Intrinsic.h>
X#include "WcCreate.h"
X#include "WcCreateP.h"
X
X#include <pwd.h>	/* to determine home dir for WcLoadResourceFileCB */
X
X/*
X*******************************************************************************
X* Private_type_declarations.
X*******************************************************************************
X*/
X
X/*
X*******************************************************************************
X* Private_macro_definitions.
X*******************************************************************************
X*/
X
X/*
X*******************************************************************************
X* Private_data_definitions.
X*******************************************************************************
X*/
X
X/* NOTE: These are shared arrays because they are large: i.e.,
X** this is a performance optimization intended to reduce page
X** faults which can occur while making large extensions to the
X** stack space.  Wait a minute: wouldn't this just happen
X** once, and then the stack space is alloc'd?  Yes, but a
X** huge stack space runs the risk of getting swapped, which causes
X** page faults.  This is probably a nit-picky sort of optimization.
X** Remember that D.Smyth is an old sys programmer from the stone
X** ages, and pity him instead of flaming him.
X** Be careful when filling msg not to call any funcs in here, 
X** so the message does not get garbled.
X*/
X
Xstatic char	msg[MAX_ERRMSG];
Xstatic char	cleanName[MAX_PATHNAME];
Xstatic Widget	widget_list[MAX_CHILDREN];
X
X/*
X*******************************************************************************
X* Private_function_declarations.
X*******************************************************************************
X*/
X
Xextern char*          getenv();
Xextern int            getuid();
Xextern struct passwd* getpwuid();
Xextern struct passwd* getpwnam();
X
X/* 
X    -- Find Home Directory
X*******************************************************************************
X    Used by WcLoadResourceFileCB.
X    Argument can be THIS_USER (a NULL) or can be a user's name.
X    The character string returned is in static storage, as returned
X    by getpwnam().
X*/
X
X#define THIS_USER ((char*)0)
X
Xstatic char* HomeDirectory( user )
X    char* user;
X{
X    struct passwd *pw;
X    char* homeDir = NULL;
X
X    if ( user == THIS_USER || *user == '\0' )
X    {
X	homeDir = getenv("HOME");
X
X        if (homeDir == NULL)
X        {
X            char* thisUser = getenv("USER");
X
X            if (thisUser == NULL)
X                pw = getpwuid( getuid() );
X            else
X                pw = getpwnam( thisUser );
X            if (pw)
X                homeDir = pw->pw_dir;
X        }
X    }
X    else
X    {
X	/* some other user */
X	pw = getpwnam( user );
X	if (pw)
X	    homeDir = pw->pw_dir;
X    }
X    return homeDir;
X}
X
X/*
X    -- Build Default User Serach Path suitable for XtResolvePathname
X*******************************************************************************
X    Used by WcLoadResourceFileCB.
X    Argument can be THIS_USER (a NULL) or can be a user's name.
X    Returns a pointer to static storage.
X*/
X
Xstatic char* DefaultUserSearchPath( user )
X    char* user;
X{
X    static char spath[3*MAX_PATHNAME + 80];
X
X    char* xApplResDir = getenv("XAPPLRESDIR");
X    char* homeDir     = HomeDirectory( user );
X
X    if (xApplResDir && homeDir)
X    {
X        sprintf( spath, "%s/%%L/%%N:%s/%%l/%%N:%s/%%N:%s/%%N",
X                xApplResDir, xApplResDir, xApplResDir, homeDir );
X    }
X    else if (xApplResDir)
X    {
X        sprintf( spath, "%s/%%L/%%N:%s/%%l/%%N:%s/%%N",
X                xApplResDir, xApplResDir, xApplResDir );
X    }
X    else if (homeDir)
X    {
X	sprintf( spath, "%s/%%L/%%N:%s/%%l/%%N:%s/%%N",
X		homeDir, homeDir, homeDir );
X    }
X    else
X    {
X	return NULL;
X    }
X    return spath;
X}
X
X/*
X*******************************************************************************
X* Public_callback_function_declarations.
X*******************************************************************************
X    The client data argument of callbacks MUST be a null terminated
X    string.  If client == (char*)0 this is an error.  The correct way
X    to pass no client information is *client == '\0'. The CvtStringToCallback
X    converter which actually causes these functions to be called (adds
X    these functions to widget's callback lists) does ensure that the
X    client data is a proper null terminated string.
X
X    Callbacks are not intended to be re-entrant nor recursive.  Many of
X    these use static buffers.
X*******************************************************************************
X*/
X
X/*
X    -- Create Dynamically Created Children from Xrm Database
X*******************************************************************************
X    This callback creates one or more specified children of a parent widget.
X    If parent name is `this' then widget invoking callback is used as the
X    parent.  Parent name can also be a wildcarded path name.  Child names
X    must be single part, specific children of the parent. Client data format:
X
X             parent, child [,child] ...
X    
X    This callback is used for deferred sub-tree creation, where named child
X    creation has been postponed because it was not included in a wcChildren
X    resource value.
X*/
X
Xstatic void CreateKids();
X
Xvoid WcCreateChildrenCB ( w, parent_children, unused )
X    Widget w;
X    char* parent_children;	/* parent + list of named children */
X    caddr_t unused;		/* call data from widget, not used */
X{
X    CreateKids(w, parent_children, "WcCreateChildrenCB", WcCreateNamedChildren);
X}
X
Xvoid WcCreatePopupsCB ( w, parent_children, unused )
X    Widget w;
X    char* parent_children;      /* parent + list of named children */
X    caddr_t unused;             /* call data from widget, not used */
X{
X    CreateKids(w, parent_children, "WcCreatePopupsCB", WcCreateNamedPopups );
X}
X
Xstatic void CreateKids ( w, parent_children, caller, CreateFunc )
X    Widget w;
X    char* parent_children;      /* parent + list of named children */
X    char* caller;		/* name of calling CB func 	   */
X    PtrFuncVoid CreateFunc;
X{
X    char*	children;
X    Widget	parent;
X
X    if ( *parent_children == NUL ) 
X    {
X	XtWarning(
X            "%s ( ) - Failed \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: No widget names provided.",
X		caller, caller);
X	return;
X    }
X
X    children = WcCleanName( parent_children, cleanName );
X
X    children = WcSkipWhitespace_Comma( children );
X
X    if ((Widget)NULL == (parent = WcFullNameToWidget( w, cleanName )) )
X    {
X	sprintf( msg,
X            "%s (%s) - Failed \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: Parent widget named `%s' not found.",
X	    caller, parent_children, caller, cleanName);
X	XtWarning( msg );
X	return;
X    }
X
X    if (*children == NUL)
X    {
X        sprintf( msg,
X            "%s (%s) - Failed \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: No children names provided.",
X	    caller, parent_children, caller );
X	XtWarning( msg );
X        return;
X    }
X
X    (*CreateFunc) ( parent, children );
X}
X
X/******************************************************************************
X**  Manage or Unmanage named widget(s)
X*******************************************************************************
X    This callback translates string passed in as client data into one or more
X    widget ids, and manages or unmanages it or them.  Client data format:
X	name [, name] ...
X    Name can include `this' and other path names.
X******************************************************************************/
X
X#ifdef FUNCTION_PROTOTYPES
Xstatic void ManageOrUnmanage( Widget, char*, char*, void (*proc)() );
X#else
Xstatic void ManageOrUnmanage();
X#endif
X
Xvoid WcManageCB ( w, widgetNames, unused )
X    Widget w;
X    char* widgetNames;
X    caddr_t unused;		/* call data from widget, not used */
X{
X    ManageOrUnmanage( w, widgetNames, "WcManageCB", XtManageChildren );
X}
X
Xvoid WcUnmanageCB ( w, widgetNames, unused )
X    Widget w;
X    char* widgetNames;
X    caddr_t unused;             /* call data from widget, not used */
X{
X    ManageOrUnmanage( w, widgetNames, "WcUnmanageCB", XtUnmanageChildren );
X}
X
Xstatic void ManageOrUnmanage ( w, widgetNames, callbackName, proc )
X    Widget w;
X    char* widgetNames;
X    char* callbackName;
X    void (*proc)();
X{
X    int         numWidgets = 0;
X    char*       s = widgetNames;
X
X    while (*s && numWidgets < MAX_CHILDREN)
X    {
X        s = WcCleanName ( s, cleanName );
X        s = WcSkipWhitespace_Comma ( s );
X        if ( widget_list[numWidgets] = WcFullNameToWidget ( w, cleanName ) )
X            numWidgets++;
X        else
X        {
X            sprintf(msg,
X            "%s (%s) - Widget `%s' ignored \n\
X             Usage:   %s ( widget_name [, widget_name] ... ) \n\
X             Problem: Could not find widget named `%s'.",
X             callbackName, widgetNames, cleanName, callbackName, cleanName);
X            XtWarning( msg );
X        }
X    }
X    if (numWidgets >= MAX_CHILDREN)
X    {
X	sprintf(msg,
X            "%s (%s) - Failed \n\
X             Usage:   %s ( widget_name [, widget_name] ... ) \n\
X             Problem: Too many widgets (more than MAX_CHILDREN).",
X             callbackName, widgetNames, callbackName);
X	XtWarning( msg );
X	numWidgets = 0;
X    }
X
X    if ( numWidgets )
X        /* proc is either XtManageChildren or XtUnmanageChildren */
X        proc ( widget_list, numWidgets );
X}
X
X/*
X    -- Manage or unamange named children callback
X*******************************************************************************
X    These callbacks translates a string passed in as client data into a parent
X    widget id, and names of children of that parent.  If parent name is
X    `this' then widget invoking callback is used as the parent.  Parent
X    name can also be a wildcarded path name.  Child names must be single
X    part, specific children of the parent. Client data format:
X
X             parent, child [,child] ...
X
X    This callback can be used as an alternate for WcManageCB but it is
X    really intended to be used to manage/unmanage children of widgets 
X    which have multiple instances, and where the parent name is `this'.
X*/
X#ifdef FUNCTION_PROTOTYPES
Xstatic void ManageOrUnmanageChildren( Widget, char*, char*, void (*proc)() );
X#else
Xstatic void ManageOrUnmanageChildren();
X#endif
X
Xvoid WcManageChildrenCB ( w, parent_children, unused )
X    Widget 	w;
X    char*	parent_children;/* client data, list of named children  */
X    caddr_t	unused;		/* call data from widget		*/
X{
X    ManageOrUnmanageChildren( w, parent_children, 
X				"WcManageChildrenCB", XtManageChildren );
X}
X
Xvoid WcUnmanageChildrenCB ( w, parent_children, unused )
X    Widget      w;
X    char*       parent_children;/* client data, list of named children  */
X    caddr_t     unused;         /* call data from widget                */
X{
X    ManageOrUnmanageChildren( w, parent_children,
X                                "WcUnmanageChildrenCB", XtUnmanageChildren );
X}
X
Xstatic void ManageOrUnmanageChildren( w, parent_children, callbackName, proc )
X    Widget w;
X    char* parent_children;      /* client data, list of named children  */
X    char* callbackName;		/* WcManageChildrenCB or WcUnmanageChildrenCB */
X    void (*proc)();		/* XtManageChildren or XtUnmanageChildren */
X{
X    int         	numWidgets = 0;
X    char*		next;
X    Widget		parent;
X
X    if ( *parent_children == NUL )
X    {
X        sprintf(msg,
X            "%s ( ) - Failed \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: No widget names provided.",
X	    callbackName, callbackName);
X	XtWarning( msg );
X        return;
X    }
X
X    next = WcCleanName( parent_children, cleanName );
X    if ((Widget)NULL == (parent = WcFullNameToWidget( w, cleanName )) )
X    {
X        sprintf( msg,
X            "%s (%s) - Failed \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: Parent widget named `%s' not found.",
X            callbackName, parent_children, callbackName, cleanName);
X        XtWarning( msg );
X        return;
X    }
X
X    while (*next && numWidgets < MAX_CHILDREN)
X    {
X        next = WcCleanName( next, cleanName );
X	if (widget_list[numWidgets] = WcChildNameToWidget( parent, cleanName ))
X	    numWidgets++;
X	else
X	{
X	    sprintf( msg,
X            "%s (%s) - Child `%s' ignored \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: Child widget named `%s' not found.",
X	    callbackName, parent_children, callbackName, cleanName);
X	    XtWarning( msg );
X	}
X    }
X
X    if (numWidgets >= MAX_CHILDREN)
X    {
X        sprintf(msg,
X            "%s (%s) - Failed \n\
X             Usage: %s ( parent, child [, child ] ...) \n\
X                    Name of parent can be `this' or wildcarded pathname, \n\
X                    Name of child must be single part name from parent. \n\
X             Problem: Too many widgets (more than MAX_CHILDREN).",
X             callbackName, parent_children, callbackName);
X        XtWarning( msg );
X        numWidgets = 0;
X    }
X
X    if ( numWidgets )
X        /* proc is either XtManageChildren or XtUnmanageChildren */
X        proc ( widget_list, numWidgets );
X}
X
X/*
X    -- Destroy named children callback
X*******************************************************************************
X    This callback translates string passed in as client data into a widget id
X    and destroys it. A comma separated list of widgets can be specified.
X    `this' means the invoking widget.
X*/
X
Xvoid WcDestroyCB ( w, widgetNames, unused )
X    Widget  w;
X    char*   widgetNames;	/* client data, widgets to be destroyed */
X    caddr_t unused;		/* call data from widget, not used	*/
X{
X    int		widget_count = MAX_CHILDREN;
X    char*	unConvertedNames;
X    int         i;
X
X    unConvertedNames = WcNamesToWidgetList ( w, widgetNames, 
X				    widget_list, &widget_count );
X    if ( unConvertedNames[0] != NUL )
X    {
X	sprintf(msg,
X            "WcDestroyCB (%s) \n\
X             Usage: WcDestroyCB ( widget [, widget ] ...) \n\
X                    Name of widget can be `this' or wildcarded pathname. \n\
X		    Problem: No widgets found named %s.",
X             widgetNames, unConvertedNames);
X        XtWarning( msg );
X    }
X
X    for (i=0; i<widget_count; i++)
X       XtDestroyWidget ( widget_list[i] );
X}
X
X/*
X    -- Parse name_res_resVal into name, res, resVal
X*******************************************************************************
X    This is a private function to WcSetValueCB()
X*/
X
Xstatic void 
XParseNameResResVal( name_res_resVal, target_name, resource, res_val )
X    char* name_res_resVal;	/* input */
X    char* target_name;		/* output */
X    char* resource;		/* output */
X    char* res_val;		/* output */
X{
X    register char *d,*s;
X
X    s = name_res_resVal;
X    /* copy from name_res_resVal into target_name[],
X    ** ignore initial whitespace, stop at trailing `:'  
X    ** Then backup to get final segment, the resource name
X    */
X    for ( s = name_res_resVal ; *s && *s <= ' ' ; s++ )
X	;	/* skip initial whitespace */
X    for ( d = target_name ; *s && *s != ':' ; s++, d++ )
X	*d = *s;
X    for ( ; target_name < d && (*d != '.' && *d != '*') ; s--, d-- )
X	;
X    *d = NUL;
X
X    /* OK, now target_name is null terminated.
X    ** s points at final `.' or `*' in name_resName, 
X    ** now we copy to resource[].
X    */
X    for ( s++, d = resource ; *s && *s != ':' ; s++, d++ )
X        *d = *s;
X    *d = NUL;
X
X    /* OK, now resource is null terminated.
X    ** s points at the `:' now we skip whitespace and the rest is value
X    ** until we hit the NUL terminator.
X    */
X    for (s++ ; *s && *s <= ' ' ; s++ )
X        ;       /* skip initial whitespace */
X    for (d = res_val ; *s ; s++, d++ )
X	*d = *s;
X    do
X	*d = NUL;	/* null terminate, delete trailing whitespace */
X    while (*--d <= ' ');
X}
X
X/*
X   -- Set Resource Value on Widget
X*******************************************************************************
X    This callback sets a resource value on the named widget.
X
X    The client data argument has a format:
X
X        target_widget_name.resource_name: resource value
X
X    The special resource value of "this" means "this widget."  Typically,
X    using "this" as the resource value is used to set the "XmNdefaultButton"
X    resource on a XmbulletinBoard, "menuBar", "workArea", etc on XmMainWindows,
X    the subMenuId resource on menuBar cascade buttons, and so on.
X
X    Just about everything this function does is related to providing
X    nice error messages.
X*/
X
Xvoid WcSetValueCB ( w, name_res_resVal, unused )
X    Widget  w;  
X    char*   name_res_resVal;		/* client data: name.resName: resVal */
X    caddr_t unused;			/* call data from wudget, not used   */
X{
X    /* Note: static buffers make this routine NON-REENTRANT!! */
X    static char    target_name[MAX_XRMSTRING];
X    static char    resource[MAX_XRMSTRING];
X    static char    res_val[MAX_XRMSTRING];
X
X    Widget	target;
X    Widget	res_widget;
X    char*	res_type;	/* must be XtFree'd */
X
X    ParseNameResResVal( name_res_resVal, target_name, resource, res_val );
X
X    /* Check for syntax error: if any of the strings are null, wrongo!
X    */
X    if ( target_name[0] == NUL || resource[0] == NUL || res_val[0] == NUL )
X    {
X        char *missing;
X
X        if (target_name[0] == NUL) missing = "target_widget_name";
X        else if (resource[0] == NUL) missing = "res_name";
X        else if (res_val[0] == NUL) missing = "res_value";
X
X        sprintf ( msg,
X            "WcSetValueCB (%s) - Failed \n\
X             Usage:   WcSetValueCB( target_widget_name.res_name: res_value ) \n\
X             Problem: Missing %s argument.",
X            name_res_resVal, missing );
X        XtWarning( msg ); 
X	return;
X    }
X
X    /* See if we can find the target widget */
X    if (! (target = WcFullNameToWidget( w, target_name )) )
X    {
X        sprintf ( msg,
X            "WcSetValueCB (%s)  \n\
X             Usage:   WcSetValueCB( target_widget_name.res_name: res_value ) \n\
X             Problem: Could not find widget named `%s'",
X             name_res_resVal, target_name );
X        XtWarning( msg );
X        return;
X    }
X
X    /* Make certain widget has this resource type */
X    if (! (res_type = WcGetResourceType( target, resource )) )
X    {
X	sprintf ( msg,
X            "WcSetValueCB (%s) - Failed \n\
X             Usage:   WcSetValueCB( target_widget_name.res_name: res_value ) \n\
X             Problem: widget %s does not have a resource named %s.",
X	name_res_resVal, target_name, resource);
X        XtWarning(msg);
X	return;
X    }
X
X    /* Try this heuristic: if the resource type has "Widget" somewhere within
X    ** it, then see if we can convert the resource value to a widget using 
X    ** WcFullNameToWidget.  This allow relative naming from the calling
X    ** widget, including `this' or ^^foobar.
X    ** WcStrStr( res_type, "Widget") returns NULL if "Widget" is not in res_type
X    ** Note that we must check for both "Widget" and "Window" - we should only
X    ** check for "Widget" but Motif has a screwed up definition for the
X    ** menuWidget resource of XmRowColumn widgets.  So, until Motif fixes
X    ** XmRowColumn.h, we need to check for both...
X    */
X    if ( WcStrStr( res_type, "Widget") 
X      || WcStrStr( res_type, "Window") )  /* wrong: but that's a menuWidget */
X    {
X	Widget	res_widget;
X
X	if (res_widget = WcFullNameToWidget( w, res_val ))
X	{
X	    XtVaSetValues( target, resource, res_widget, NULL );
X	    return;
X	}
X    }
X
X    /* We have the type, so do conversion, and then call XtSetValues. */
X
X    WcSetValueFromStringAndType( target, resource, res_val, res_type );
X    XtFree( res_type );
X}
X
X/*
X    -- Parse name_res_resType_resVal into name, res, resType, resVal
X*******************************************************************************
X    This is a private function to WcSetTypeValueCB()  which *must*
X    allocate sufficient space for the return values!
X*/
X
Xstatic void 
XParseNameResResTypeResVal( name_res_resType_resVal, name, res, resType, resVal )
X    char* name_res_resType_resVal;	/* input */
X    char* name;				/* output */
X    char* res;				/* output */
X    char* resType;			/* output */
X    char* resVal;			/* output */
X{
X    register char *d,*s;
X
X    /* copy from name_res_resType_resVal into name[],
X    ** ignore initial whitespace, stop at trailing `:'  
X    ** Then backup to get final segment, the resource name
X    */
X    for ( s = name_res_resType_resVal ; *s && *s <= ' ' ; s++ )
X	;	/* skip initial whitespace */
X    for ( d = name ; *s && *s != ':' ; s++, d++ )
X	*d = *s;
X    for ( ; name < d && (*d != '.' && *d != '*') ; s--, d-- )
X	;
X    *d = NUL;
X
X    /* OK, now name is null terminated.
X    ** s points at final `.' or `*' in name_resName, 
X    ** now we copy to res[].
X    */
X    for ( s++, d = res ; *s && *s != ':' ; s++, d++ )
X        *d = *s;
X    *d = NUL;
X
X    /* OK, now resource is null terminated.
X    ** s points at the `:' now we skip whitespace and the next word is Type.
X    */
X    for (s++ ; *s && *s <= ' ' ; s++ )
X        ;       /* skip initial whitespace */
X    for ( d = resType ; *s && *s > ' ' && *s != ':' && *s != ',' ; s++, d++ )
X	*d = *s;
X    *d = NUL;
X    
X    /* OK, now resType is null terminated.
X    ** s points at the `:' or `,' or whitespace.  Skip whitespace and the 
X    ** rest is resVal until we hit the NUL terminator.
X    */
X    for (s++ ; *s && *s <= ' ' ; s++ )
X        ;       /* skip initial whitespace */
X    for (d = resVal ; *s ; s++, d++ )
X	*d = *s;
X    do
X	*d = NUL;	/* null terminate, delete trailing whitespace */
X    while (*--d <= ' ');
X}
X
X/*
X   -- Set Resource Value on Widget using specific target type info
X*******************************************************************************
X    This callback sets a resource value on the named widget.  It exists
X    so sub-resources can be set.  Specifically, widgets which have
X    non-widget components (XawText and XmText are classic examples)
X    can accept resources which are NOT returned by XtGetResourceList
X    or by XtGetConstraintResourceList.  Probably, this is a bug in
X    the widget's GetValuesHook.
X
X    The client data argument has a format:
X
X        target_widget_name.resource_name: resType, resource value
X
X    The resource type, unfortunately, *must* be the string represented
X    by such constants as XmRString or XtRPixel.  Sorry.
X
X    The special resource value of "this" means "this widget."  Typically,
X    using "this" as the resource value is used to set the "XmNdefaultButton"
X    resource on a XmbulletinBoard, "menuBar", "workArea", etc on XmMainWindows,
X    the subMenuId resource on menuBar cascade buttons, and so on.
X
X    Just about everything this function does is related to providing
X    nice error messages.
X*/
X
Xvoid WcSetTypeValueCB ( w, name_res_resType_resVal, unused )
X    Widget  w;  
X    char*   name_res_resType_resVal;	/* wid.res: type, val	*/
X    caddr_t unused;			/* call data, not used	*/
X{
X    /* Note: static buffers make this routine NON-REENTRANT!! */
X    static char    name   [MAX_XRMSTRING];
X    static char    res    [MAX_XRMSTRING];
X    static char    resType[MAX_XRMSTRING];
X    static char    resVal [MAX_XRMSTRING];
X
X    Widget	target;
X
X    *name = *res = *resType = *resVal = NUL;
X
X    ParseNameResResTypeResVal( name_res_resType_resVal, 
X				name, res, resType, resVal);
X
X    /* Check for syntax error: if any of the strings are null, wrongo!
X    */
X    if ( *name == NUL || *res == NUL || *resType == NUL || *resVal == NUL )
X    {
X        char *missing;
X
X        if (     *name    == NUL) missing = "target_widget";
X        else if (*res     == NUL) missing = "res";
X	else if (*resType == NUL) missing = "resType";
X        else if (*resVal  == NUL) missing = "resVal";
X
X        sprintf ( msg,
X            "WcSetTypeValueCB (%s) - Failed \n\
X             Usage:   WcSetTypeValueCB( target_widget.res: resType, resVal )\n\
X             Problem: Missing %s argument.",
X            name_res_resType_resVal, missing );
X        XtWarning( msg ); 
X	return;
X    }
X
X    /* See if we can find the target widget */
X    if (! (target = WcFullNameToWidget( w, name )) )
X    {
X        sprintf ( msg,
X            "WcSetTypeValueCB (%s)  \n\
X             Usage:   WcSetTypeValueCB( target_widget.res: resType, resVal )\n\
X             Problem: Could not find widget named `%s'",
X             name_res_resType_resVal, name );
X        XtWarning( msg );
X        return;
X    }
X
X    /* Try this heuristic: if the resource type has "Widget" somewhere within
X    ** it, then see if we can convert the resource value to a widget using 
X    ** WcFullNameToWidget.  This allow relative naming from the calling
X    ** widget, including `this' or ^^foobar.
X    ** WcStrStr( res_type, "Widget") returns NULL if "Widget" is not in res_type
X    ** Note that we must check for both "Widget" and "Window" - we should only
X    ** check for "Widget" but Motif has a screwed up definition for the
X    ** menuWidget resource of XmRowColumn widgets.  So, until Motif fixes
X    ** XmRowColumn.h, we need to check for both...
X    */
X    if ( WcStrStr( resType, "Widget") 
X      || WcStrStr( resType, "Window") )  /* wrong: but that's a menuWidget */
X    {
X	Widget	res_widget;
X
X	if (res_widget = WcFullNameToWidget( w, resVal ))
X	{
X	    XtVaSetValues( target, res, res_widget, NULL );
X	    return;
X	}
X    }
X
X    /* We have the type, so do conversion, and then call XtSetValues. */
X
X    WcSetValueFromStringAndType( target, res, resVal, resType );
X}
X
X/*
X    -- Change sensitivity of widgets.
X*******************************************************************************
X    This callback translates string passed in as client data into widget ids
X    and sets each to be sensitve/insensitive. A comma separated list of 
X    widgets can be specified.  `this' means the invoking widget.  
X
X    This callback someday should take care of the problem with text
X    widgets - they don't get grey when insensitive, which must be a bug.
X*/
X
X#ifdef FUNCTION_PROTOTYPES
Xstatic void ChangeSensitivity( Widget, char*, char*, int );
X#else
Xstatic void ChangeSensitivity();
X#endif
X
Xvoid WcSetSensitiveCB ( w, widgetNames, unused )
X    Widget  w;
X    char*   widgetNames;        /* client data, widgets to be destroyed */
X    caddr_t unused;		/* call data from widget is not used    */
X{
X    ChangeSensitivity ( w, widgetNames, "WcSetSensitiveCB", (Boolean)TRUE );
X}
X
Xvoid WcSetInsensitiveCB ( w, widgetNames, unused )
X    Widget  w;
X    char*   widgetNames;        /* client data, widgets to be destroyed */
X    caddr_t unused;             /* call data from widget is not used    */
X{
X    ChangeSensitivity ( w, widgetNames, "WcSetInsensitiveCB", (Boolean)FALSE );
X}
X
Xstatic void ChangeSensitivity ( w, widgetNames, callbackName, sensitive )
X    Widget  w;
X    char*   widgetNames;        /* client data, widgets to be destroyed */
X    char*   callbackName;	/* "WcSetSensitiveCB" or "WcSetInsensitiveCB" */
X    int     sensitive;
X{
X    int    widget_count = MAX_CHILDREN;
X    char*  unConvertedNames;
X    int    i;
X
X    unConvertedNames = WcNamesToWidgetList ( w, widgetNames,
X                                    widget_list, &widget_count );
X    if ( unConvertedNames[0] != NUL )
X    {
X        sprintf(msg,
X            "%s (%s) - One or more widget names ignored \n\
X             Usage: %s ( widget [, widget ] ...) \n\
X                    Name of widget can be `this' or wildcarded pathname. \n\
X                    Problem: No widgets found named %s.",
X             callbackName, widgetNames, callbackName, unConvertedNames);
X        XtWarning( msg );
X    }
X
X    for (i=0; i<widget_count; i++)
X	XtSetSensitive ( widget_list[i], sensitive );
X}
X
X/*
X    -- Load Resource File
X*******************************************************************************
X    This callbacks loads specified resource file into application
X    resource database. It allows to load resources on as-needed
X    basis, reducing the intitial resource file load overhead. 
X    The file to load is specified as client data. The directory search 
X    for the file (should be) the same as for application class resource file.
X    
X    To prevent repeated loads of the same file, the callback keeps
X    track of each filename.  Note that I do not allow a file to be
X    re-loaded even if it is changed, or if a new file of the same 
X    name appears on the search path.  This was done for two reasons:
X    first, it makes the code more portable, as I don't have to depend
X    upon various system calls.  Second, resources can't be un-written,
X    so a user might get the wrong impression that a resource specification
X    can be deleted, and the resource file re-loaded, and something will
X    happen.  It just isn't so.
X
X    NOTE:
X    The file search list rule used here is a gross simplification of the R3
X    resource file search mechanism, without the $LANG provision.
X    I hope I can use R4 soon and do it RIGHT, but I depend on Motif for now,
X    and do not want to duplicate all the Motif code here.
X    Here I look into two directories only, which may be defined as environmental
X    variables:
X         XAPPLRESDIR  - defaults to "/usr/lib/X11/app-defaults/"
X	 XUSERRESDIR  - defaults to HOME directory
X*/
X
Xvoid WcLoadResourceFileCB ( w,  resFileName, unused )
X    Widget w;
X    char*  resFileName;	/* client data, X resources file name */
X    caddr_t unused;	/* call data,   not used */
X{
X    static char		name[MAX_PATHNAME];		/* so not on stack */
X    XrmQuark		nameQ;
X    static XrmQuark	nameQuarks[MAX_RES_FILES];	/* initially all 0 */
X    int			i;
X    XrmDatabase		rdb;
X    Display*		dpy = XtDisplay(w);
X
X    (void) WcCleanName( resFileName, name );
X
X/*  -- check pathname presence */
X    if ( *name == NUL )
X    {
X        XtWarning ( 
X            "WcLoadResourceFileCB () - Failed \n\
X             Usage:   WcLoadResourceFileCB( resource_file_name ) \n\
X             Problem: No file name provided.");
X        return;
X    }
X
X/*  -- check for repeated load, remember if first load */
X    nameQ = XrmStringToQuark( name );
X    i = 0;
X    while ( nameQuarks[i] && i < MAX_RES_FILES )
X    {
X	if ( nameQuarks[i] == nameQ )
X	    return;			/* NB: not an error, its even common */
X	else
X	    i++;
X    }
X    nameQuarks[i] = nameQ;
X
X/*  -- See if filename is an absolute pathname from root `/' */
X    if ( '/' == name[0] )
X    {
X	if ((rdb = XrmGetFileDatabase( name )) != NULL )
X	    XrmMergeDatabases (rdb, &(dpy->db) );
X	else
X	{
X	    sprintf( msg,
X            "WcLoadResourceFileCB (%s) - Failed \n\
X             Usage:   WcLoadResourceFileCB( resource_file_name ) \n\
X             Problem: file %s is not a readable file.", 
X	    resFileName, name );
X	    XtWarning( msg );
X	}
X	return;
X    }
X
X/*  -- See if filename is a pathname from tilda */
X    if ( '~' == name[0] )
X    {
X	char* homeDir;
X	char  path[ MAX_PATHNAME ];
X	char  user[ MAX_PATHNAME ];
X	char* from = &name[1];		/* skip the tilda */
X	char* to   = &user[0];
X
X	while (*from && *from != '/')
X	    *to++ = *from++;
X	*to = '\0';
X
X	/* NB: `user' now contains either a NUL, or the characters 
X	** between the `~' and the first `/`.  `from' now points into 
X	** `name' after the `~user', right at the `/` 
X	*/
X
X	homeDir = HomeDirectory( user );
X
X	if( strlen(homeDir) + strlen(from) >= MAX_PATHNAME )
X	{
X	    sprintf( msg,
X            "WcLoadResourceFileCB (%s) - Failed \n\
X             Usage:   WcLoadResourceFileCB( resource_file_name ) \n\
X             Problem: file %s becomes too long when expanded.",
X            resFileName, name );
X            XtWarning( msg );
X	    return;
X	}
X
X	strcpy( path, homeDir );
X	strcat( path, from );
X
X	if ((rdb = XrmGetFileDatabase( path )) != NULL )
X	    XrmMergeDatabases (rdb, &(dpy->db) );
X        else
X        {
X            sprintf( msg,
X            "WcLoadResourceFileCB (%s) - Failed \n\
X             Usage:   WcLoadResourceFileCB( resource_file_name ) \n\
X             Problem: file %s is not a readable file.",
X            resFileName, path );
X            XtWarning( msg );
X        }
X        return;
X    }
X
X/*  -- Look for file in current working directory.
X**     Note this handles when name begins with `.'
X*/
X
X    if ((rdb = XrmGetFileDatabase(name)) != NULL )
X    {
X        XrmMergeDatabases (rdb, &(dpy->db) );
X        return;
X    }
X
X
X#ifdef XtSpecificationRelease
X    {
X	/* Use XUSERFILESEARCHPATH, user's home directory, and XAPPLRESDIR 
X	** in the same way that XtR4 does when it gets user's application 
X	** defaults.  This code basically mimics GetAppUserDefaults() in 
X	** mit/lib/Xt/Initialize.c of the X11R4 distribution.  The main
X	** difference is that this routine uses the filename argument `name' 
X	** instead of the application class name.
X	*/
X	char* filename;
X	char* path;
X	char* xUserFileSearchPath = getenv("XUSERFILESEARCHPATH");
X
X	if (xUserFileSearchPath)
X	    path = xUserFileSearchPath;
X	else
X	    path = DefaultUserSearchPath( THIS_USER );
X
X	filename = XtResolvePathname(
X		dpy,			/* could be used for language	*/
X		"apps-defaults",	/* type, used as sub-directory	*/
X		name,			/* file name			*/
X		NULL,			/* no file name suffix		*/
X		path,			/* search path, may be NULL	*/
X		NULL, 0,		/* no additional path substitutions */
X		NULL );			/* if the file exists, is readable,
X					** and not a directory, its OK	    */
X	if ((rdb = XrmGetFileDatabase(filename)) != NULL )
X	{
X	    XrmMergeDatabases (rdb, &(dpy->db) );
X	    return;
X	}
X    }
X
X#else
X    {
X	/* For Motif 1.0, do something really simple and stupid.  Look for the
X	** file in XAPPLRESDIR which is defined in the user's environment, or 
X	** in XAPPLOADDIR, the site defined directory for applcation defaults 
X	** (commonly /usr/lib/X11/app-defaults
X	*/
X	char  filename[ MAX_PATHNAME ];
X	char* path = getenv("XAPPLRESDIR");
X
X        if ( NULL == path )
X	    path = XAPPLOADDIR;
X
X        if( strlen(path) + strlen(name) >= MAX_PATHNAME )
X        {
X            sprintf( msg,
X            "WcLoadResourceFileCB (%s) - Failed \n\
X             Usage:   WcLoadResourceFileCB( resource_file_name ) \n\
X             Problem: file %s becomes too long when \n\
X                      prepended with %s.",
X            resFileName, name, path );
X            XtWarning( msg );
X            return;
X        }
X
X        strcpy ( filename, path );
X        strcat ( filename, name );
X
X        if ((rdb = XrmGetFileDatabase(filename)) != NULL )
X        {
X	    XrmMergeDatabases (rdb, &(dpy->db) );
X	    return;
X        }	
X    }
X#endif
X
X/*  -- warn the user if no file found */
X    sprintf  ( msg, 
X            "WcLoadResourceFileCB (%s) - Failed \n\
X             Usage:   WcLoadResourceFileCB( resource_file_name ) \n\
X             Problem: Cannot find resource file %s",
X	resFileName, name );
X    XtWarning( msg );
X}
X
X/*
X  -- WcTraceCallback
X*******************************************************************************
X    This is a simple traceback callback, used to assist in interface
X    debugging. The callback prints the invoking wiget pathname and
X    a specified message on std. output.
X*/
X
Xvoid WcTraceCB ( w, annotation, unused )
X    Widget w;
X    char* annotation;	/* client data, traceback annotation */
X    caddr_t unused;	/* call data,   not used */
X{
X    char* name = WcWidgetToFullName( w );
X    
X    printf("TraceCB for %s: %s\n", name, annotation );
X    XtFree( name );
X}
X
X/*
X  -- Popup named widget
X*******************************************************************************
X    These callbacks translate a string passed in as client data into a 
X    widget id.  
X
X    A grab kind value of XtGrabNone has the effect of allowing 
X    non-modal popups.  This is the preferred type: rarely use modal pop-ups.
X    This is registered as PopupCB.
X
X    A grab kind value of XtGrabExclusive has the effect of grabbing all
X    application events, allowing modal popups.  This is registered as 
X    PopupGrabCB.
X*/
X
X#ifdef FUNCTION_PROTOTYPES
Xstatic void Popup ( Widget, char*, char*, XtGrabKind );
X#else
Xstatic void Popup ();
X#endif
X
Xvoid WcPopupCB ( w, name, unused )
X    Widget      w;
X    char*       name;
X    caddr_t	unused;
X{
X    Popup ( w, name, "WcPopupCB", XtGrabNone );
X}
X
Xvoid WcPopupGrabCB ( w, name, unused )
X    Widget      w;
X    char*       name;
X    caddr_t     unused;
X{
X    Popup ( w, name, "WcPopupGrabCB", XtGrabExclusive );
X}
X
Xstatic void Popup ( w, name, callbackName, grab )
X    Widget	w;
X    char*	name;
X    char*	callbackName;
X    XtGrabKind	grab;
X{
X    Widget      widget;
X
X    (void)WcCleanName ( name, cleanName );
X    widget = WcFullNameToWidget ( w, cleanName );
X
X    if (XtIsShell(widget))
X    {
X        XtPopup  ( widget, grab );
X    }
X    else
X    {
X        sprintf( msg,
X            "%s (%s) - Failed \n\
X             Usage: %s (shell_widget_name) \n\
X             Problem: `%s' is not a shell widget.",
X             callbackName, name, callbackName, cleanName);
X        XtWarning( msg );
X    }
X}
X
X/*
X  -- Popdown named widget
X*******************************************************************************
X    This callback translates string passed in as client data into a widget id
X    and pops-down a popup shell widget.
X*/
X
Xvoid WcPopdownCB ( w, name, unused ) 
X    Widget	w;
X    char*	name;
X    caddr_t	unused;
X{
X    Widget      widget;
X
X    (void)WcCleanName ( name, cleanName );
X    widget = WcFullNameToWidget ( w, cleanName );
X
X    if (XtIsShell(widget))
X    {
X        XtPopdown  ( widget );
X    }
X    else
X    {
X        sprintf( msg,
X            "WcPopdownCB (%s) Failed \n\
X             Usage: WcPopdownCB (shell_widget_name) \n\
X             Problem: `%s' is not a shell widget.",
X             name, cleanName);
X        XtWarning( msg );
X    }
X}
X
X/*
X  -- Map and Unmap named widget
X*******************************************************************************
X    These callbacks translate a string passed as client data into a widget id
X    and invokes either XtMapWidget() os XtUnmapWidget() as appropriate.
X*/
X
Xvoid WcMapCB (w, name, unused )
X    Widget      w;
X    char*       name;
X    caddr_t     unused;
X{
X    Widget      widget;
X
X    (void)WcCleanName ( name, cleanName );
X    widget = WcFullNameToWidget ( w, cleanName );
X
X    if (XtIsShell(widget))
X    {
X        XtMapWidget ( widget );
X    }
X    else
X    {
X        sprintf( msg,
X            "WcMapCB (%s) Failed \n\
X             Usage: WcMapCB (shell_widget_name) \n\
X             Problem: `%s' is not a shell widget.",
X             name, cleanName);
X        XtWarning( msg );
X    }
X}
X
Xvoid WcUnmapCB (w, name, unused )
X    Widget      w;
X    char*       name;
X    caddr_t     unused;
X{
X    Widget      widget;
X
X    (void)WcCleanName ( name, cleanName );
X    widget = WcFullNameToWidget ( w, cleanName );
X
X    if (XtIsShell(widget))
X    {
X        XtUnmapWidget ( widget );
X    }
X    else
X    {
X        sprintf( msg,
X            "WcUnmapCB (%s) Failed \n\
X             Usage: WcUnmapCB (shell_widget_name) \n\
X             Problem: `%s' is not a shell widget.",
X             name, cleanName);
X        XtWarning( msg );
X    }
X}
X
X/*
X  -- Invoke shell command
X*******************************************************************************
X    Call system().
X*/
X
Xvoid WcSystemCB ( w, shellCmdString, unused )
X    Widget      w;
X    char*       shellCmdString;
X    caddr_t     unused;
X{
X    system( shellCmdString );
X}
X
X/*
X  -- Exit the application
X*******************************************************************************
X    Call exit().
X*/
X
Xvoid WcExitCB ( w, exitValString, unused )
X    Widget	w;
X    char*	exitValString;
X    caddr_t	unused;
X{
X    int exitval = 0;
X
X    /* skip leading garbage before int */
X    while (*exitValString)
X    {
X        if ('0' < *exitValString && *exitValString <= '9')
X            break; /* found numbers, convert to exitval */
X        exitValString++;
X    }
X
X    /* convert to int */
X    while (*exitValString)
X    {
X        if ('0' < *exitValString && *exitValString <= '9')
X        {
X            exitval = exitval * 10 + (*exitValString - '0');
X            exitValString++;
X        }
X        else
X            break;  /* ignore trailing garbage */
X    }
X
X    exit( exitval );
X}
X
X/*
X  -- WcRegisterCreateCallbacks
X*******************************************************************************
X   Convenience routine, registering all standard callbacks in one application
X   call.   Called from WcWidgetCreation(), so application usually never needs
X   to call this.
X*/
X
Xvoid WcRegisterWcCallbacks ( app )
XXtAppContext app;
X{
X    ONCE_PER_XtAppContext( app );
X
X#define RCALL( name, func ) WcRegisterCallback ( app, name, func, NULL )
X
X    RCALL( "WcCreateChildrenCB",	WcCreateChildrenCB	);
X    RCALL( "WcCreatePopupsCB",		WcCreatePopupsCB	);
X    RCALL( "WcManageCB",		WcManageCB		);
X    RCALL( "WcUnmanageCB",		WcUnmanageCB		);
X    RCALL( "WcManageChildrenCB",	WcManageChildrenCB	);
X    RCALL( "WcUnmanageChildrenCB",	WcUnmanageChildrenCB	);
X    RCALL( "WcDestroyCB",		WcDestroyCB		);
X    RCALL( "WcSetValueCB",		WcSetValueCB		);
X    RCALL( "WcSetTypeValueCB",		WcSetTypeValueCB	);
X    RCALL( "WcSetSensitiveCB",		WcSetSensitiveCB	);
X    RCALL( "WcSetInsensitiveCB",	WcSetInsensitiveCB	);
X    RCALL( "WcLoadResourceFileCB",	WcLoadResourceFileCB	);
X    RCALL( "WcTraceCB",			WcTraceCB		);
X    RCALL( "WcPopupCB",			WcPopupCB		);
X    RCALL( "WcPopupGrabCB",		WcPopupGrabCB		);
X    RCALL( "WcPopdownCB",		WcPopdownCB		);
X    RCALL( "WcMapCB",			WcMapCB			);
X    RCALL( "WcUnmapCB",			WcUnmapCB		);
X    RCALL( "WcSystemCB",		WcSystemCB		);
X    RCALL( "WcExitCB",			WcExitCB		);
X}
!STUFFY!FUNK!
echo Extracting Mri/M11_FSB
sed >Mri/M11_FSB <<'!STUFFY!FUNK!' -e 's/X//'
XMri.wcChildren:		fsb
XMri.title:		M11_FSB
X
X*fsb.wcConstructor:	XmCreateFileSelectionBox
X*fsb.dirMask:		Mri
!STUFFY!FUNK!
echo " "
echo "End of kit 22 (of 35)"
cat /dev/null >kit22isdone
run=''
config=''
for iskit in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35; do
    if test -f kit${iskit}isdone; then
	run="$run $iskit"
    else
	todo="$todo $iskit"
    fi
done
case $todo in
    '')
	echo "You have run all your kits.  Please read README."
	for combo in `find . -name '*:AA' -print`; do
	    if test -f "$combo"; then
		realfile=`echo $combo | sed 's/:AA$//'`
		cat $realfile:[A-Z][A-Z] >$realfile
		rm -rf $realfile:[A-Z][A-Z]
	    fi
	done
	rm -rf kit*isdone
	chmod ugo+x test*
	;;
    *)  echo "You have run$run."
	echo "You still need to run$todo."
	;;
esac
: Someone might mail this, so...
exit

--
dan
----------------------------------------------------
O'Reilly && Associates   argv@sun.com / argv@ora.com
Opinions expressed reflect those of the author only.