5466 lines
164 KiB
C
5466 lines
164 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these librararies and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $TOG: Action.c /main/28 1999/09/16 14:55:25 mgreess $ */
|
|
/*
|
|
* (c) Copyright 1997, The Open Group
|
|
*/
|
|
/*************************************<+>*************************************
|
|
*****************************************************************************
|
|
**
|
|
** File: Action.c
|
|
**
|
|
** Project: DT
|
|
**
|
|
** Description: This file contains the action library source code.
|
|
**
|
|
**
|
|
** (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
** (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
** (c) Copyright 1993, 1994 Novell, Inc.
|
|
**
|
|
**
|
|
****************************************************************************
|
|
************************************<+>*************************************/
|
|
|
|
/*LINTLIBRARY*/
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/param.h>
|
|
|
|
#ifdef _SUN_OS /* Need this for the strtod () call */
|
|
#include <floatingpoint.h>
|
|
#endif /* _SUN_OS */
|
|
|
|
#define X_INCLUDE_STRING_H
|
|
#define XOS_USE_XT_LOCKING
|
|
#include <X11/Xos_r.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
|
|
#include <X11/Intrinsic.h>
|
|
|
|
#include <Dt/DtP.h>
|
|
#include <Dt/Dts.h>
|
|
#include <Dt/Help.h>
|
|
#include <Dt/Message.h>
|
|
#include <Dt/Connect.h>
|
|
#include <Dt/Indicator.h>
|
|
#include <Dt/DtNlUtils.h>
|
|
#include <Dt/CommandM.h>
|
|
#include <Dt/Utility.h>
|
|
#include <Dt/Service.h>
|
|
#include <Dt/UserMsg.h>
|
|
|
|
#include <Xm/Xm.h>
|
|
#include <Xm/BulletinB.h>
|
|
#include <Xm/DialogS.h>
|
|
#include <Xm/Frame.h>
|
|
#include <Xm/Form.h>
|
|
#include <Xm/LabelG.h>
|
|
#include <Xm/TextF.h>
|
|
#include <Xm/SeparatoG.h>
|
|
#include <Xm/PushBG.h>
|
|
#include <Xm/MessageB.h>
|
|
#include <Xm/MwmUtil.h>
|
|
#include <Xm/Protocols.h>
|
|
|
|
#include <Dt/ActionP.h>
|
|
#include <Dt/ActionUtilP.h>
|
|
#include <Dt/ActionDb.h>
|
|
#include <Dt/ActionFind.h>
|
|
#include <Tt/tttk.h>
|
|
|
|
#include <Dt/Action.h>
|
|
|
|
#include "myassertP.h"
|
|
#include "DtSvcLock.h"
|
|
|
|
#ifndef CDE_INSTALLATION_TOP
|
|
#define CDE_INSTALLATION_TOP "/opt/dt"
|
|
#endif
|
|
|
|
extern char * _DtStripSpaces(
|
|
char * string) ;
|
|
extern char * _DtDbPathIdToString( DtDbPathId pathId) ;
|
|
|
|
#define _MAX_MAP_ATTEMPTS 100 /* Maximum nuber of "MAPS" that will
|
|
be done. */
|
|
#define _DT_ACTION_MAX_CLOSE_TRIES 5
|
|
|
|
/******** Public Function Declarations ********/
|
|
|
|
void _DtCreateErrorDialog(
|
|
Widget w,
|
|
char * actionName,
|
|
XmString msg) ;
|
|
Boolean _DtCompileMessagePiece(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir,
|
|
parsedMsg * piece,
|
|
Boolean initialize,
|
|
unsigned long processingMask,
|
|
Boolean ** paramUsed,
|
|
int * promptDataIndex ) ;
|
|
ActionRequest * _DtCloneRequest (
|
|
ActionRequest * request) ;
|
|
void _DtFreeRequest(
|
|
register ActionRequest *request) ;
|
|
|
|
char * _DtFindCwd( void ) ;
|
|
|
|
char * _DtActMapFileName(
|
|
const char * curHost,
|
|
const char * dir,
|
|
const char * file,
|
|
const char * newHost ) ;
|
|
|
|
extern void _DtProcessTtRequest(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir ) ;
|
|
extern Tt_status _DtInitializeToolTalk(Widget w);
|
|
|
|
extern Boolean _DtEmptyString(
|
|
String str) ;
|
|
|
|
/******** End Public Function Declarations ********/
|
|
|
|
|
|
/******** Static Function Declarations ********/
|
|
|
|
|
|
static void FreeErrorDialog(
|
|
Widget w,
|
|
XtPointer user_data,
|
|
XtPointer call_data) ;
|
|
static void InvalidFilename(
|
|
Widget w,
|
|
char * actionName,
|
|
char * filename) ;
|
|
static void HostAccessError(
|
|
Widget w,
|
|
char * actionName,
|
|
char * hostName) ;
|
|
static void MultiHostAccessError(
|
|
Widget w,
|
|
char * actionName,
|
|
char * hostList) ;
|
|
static void NoActionError(
|
|
Widget w,
|
|
DtShmBoson origNameQuark,
|
|
char * actionName,
|
|
char * type,
|
|
char * host,
|
|
char * dir,
|
|
char * file) ;
|
|
static void MapError(
|
|
Widget w,
|
|
char * actionName ) ;
|
|
static void CommandInvokerError(
|
|
Widget w,
|
|
char * actionName,
|
|
char * errorString) ;
|
|
static void NoToolTalkConnectionError(
|
|
Widget w,
|
|
String actionName,
|
|
Tt_status status) ;
|
|
static void TmpFileCreateError(
|
|
Widget w,
|
|
char *actionName,
|
|
char *dirName) ;
|
|
static void TmpFileOpenError(
|
|
Widget w,
|
|
char *actionName,
|
|
char *fileName) ;
|
|
static void TmpFileWriteError(
|
|
Widget w,
|
|
char *actionName,
|
|
char *fileName) ;
|
|
static void UnSupportedObject(
|
|
Widget w,
|
|
char *actionName,
|
|
int objClass);
|
|
static void SetExecHost(
|
|
ActionRequest * request) ;
|
|
static void ParseHostList (
|
|
char * hostString,
|
|
char *** hostListPtr,
|
|
int * hostListSizePtr,
|
|
int * hostCountPtr) ;
|
|
static void RemoveDuplicateHostNames (
|
|
char ** hostList,
|
|
int * hostCountPtr ) ;
|
|
static void AddFailedHostToList (
|
|
ActionRequest * request,
|
|
char * badHost) ;
|
|
static int _DtAddEntry(
|
|
register char * string,
|
|
register char * **arrayPtr,
|
|
register int *sizePtr) ;
|
|
static void TryToTypeFile(
|
|
ObjectData *obj,
|
|
char * host,
|
|
char * dir,
|
|
char * file,
|
|
char ** resolvedPath);
|
|
static ActionRequest * CreateActionRequest(
|
|
Widget w,
|
|
char * actionName,
|
|
DtActionArg *aap,
|
|
int numArgs,
|
|
char * termOpts,
|
|
char * execHost,
|
|
char * cwdHost,
|
|
char * cwdDir,
|
|
_DtActInvRecT *invp);
|
|
static _DtActInvRecT *CreateInvocationRecord(
|
|
char *actionName,
|
|
Widget w,
|
|
DtActionArg *aap,
|
|
int numArgs);
|
|
static Boolean ParseFileArgument(
|
|
Widget w,
|
|
ActionRequest * request,
|
|
ObjectData * objectData,
|
|
char * hostname,
|
|
char * filename,
|
|
char * filetype,
|
|
Boolean typeFile) ;
|
|
static void AddPrompt(
|
|
register int argNum,
|
|
char * prompt,
|
|
register int *numPrompts,
|
|
register PromptEntry **prompts) ;
|
|
static int MatchParamsToAction(
|
|
ActionRequest *request,
|
|
int *numPrompts,
|
|
PromptEntry **prompts) ;
|
|
static void ProcessOneSegment(
|
|
ActionRequest * request,
|
|
parsedMsg * msg,
|
|
PromptEntry **prompts,
|
|
int *numPrompts,
|
|
Boolean * argsOptionFound,
|
|
int * lastArgReferenced,
|
|
int * unused,
|
|
Boolean * paramUsed) ;
|
|
static ActionPtr CloneActionDBEntry(
|
|
register ActionPtr action) ;
|
|
static void CloneParsedMessage(
|
|
register parsedMsg * old_pmsg,
|
|
register parsedMsg * new_pmsg ) ;
|
|
static void FreeParsedMessage(
|
|
register parsedMsg * parsedMessage) ;
|
|
static parsedMsg * CloneParsedMessageArray(
|
|
register parsedMsg * pmsgArray,
|
|
register int count ) ;
|
|
static void FreeParsedMessageArray(
|
|
register parsedMsg * parsedMessageArray,
|
|
int count ) ;
|
|
static Boolean InsertArgumentString(
|
|
Widget w,
|
|
register char **bufPtr,
|
|
int * bufSizePtr,
|
|
ActionRequest *request,
|
|
register ObjectData *object,
|
|
unsigned long mask,
|
|
char * relPathHost,
|
|
char * relPathDir,
|
|
Boolean addLeadingSpace,
|
|
unsigned long processingMask ) ;
|
|
static void InsertUnmappedArgumentString(
|
|
register char **bufPtr,
|
|
int * bufSizePtr,
|
|
register ObjectData *object,
|
|
Boolean addLeadingSpace ) ;
|
|
static char * GrowMsgBuffer(
|
|
char * buffer,
|
|
int *size,
|
|
int count) ;
|
|
static void CmdInvSuccessfulRequest(
|
|
char *message,
|
|
void *data2) ;
|
|
static void CmdInvFailedRequest(
|
|
char *message,
|
|
void *data2) ;
|
|
static void InitiateDtRequest(
|
|
Widget w,
|
|
ActionRequest *request) ;
|
|
static Boolean ResolveDtNotifyMessagePieces(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir ) ;
|
|
static void InitiateDtNotifyMessage(
|
|
Widget w,
|
|
ActionRequest *request ) ;
|
|
static void PrepareAndExecuteAction(
|
|
Widget w,
|
|
register ActionRequest *request);
|
|
static void __ExtractCWD(
|
|
register ActionRequest *request,
|
|
char ** hostPtr,
|
|
char ** dirPtr,
|
|
Boolean useObjectInfo) ;
|
|
static void ContinueRequest(
|
|
Widget widget,
|
|
XtPointer user_data,
|
|
XtPointer call_data) ;
|
|
static void CancelRequest(
|
|
Widget widget,
|
|
XtPointer user_data,
|
|
XtPointer call_data) ;
|
|
static void CreateContinueDialog(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
int numPrompts,
|
|
register PromptEntry *prompts) ;
|
|
static void CancelPromptDialog(
|
|
Widget widget,
|
|
PromptDialog *dialog,
|
|
XtPointer call_data) ;
|
|
static void ProcessPromptDialog(
|
|
Widget widget,
|
|
register PromptDialog *dialog,
|
|
XtPointer call_data) ;
|
|
static void ChangePromptTraversal(
|
|
Widget widget,
|
|
register PromptDialog *dialog,
|
|
XtPointer call_data) ;
|
|
static void CreatePromptDialog(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
register int numPrompts,
|
|
register PromptEntry *prompts) ;
|
|
static Boolean MoreArgumentsToProcess(
|
|
register ActionRequest *request) ;
|
|
static Boolean ProcessRequest(
|
|
Widget w,
|
|
register ActionRequest *request) ;
|
|
static void InitLocalizedStrings( void ) ;
|
|
static int LinkToTypeQuark(
|
|
char * host,
|
|
char * dir,
|
|
char * file,
|
|
char **resolvedPath) ;
|
|
static void CancelOut(
|
|
Widget w,
|
|
XEvent *event,
|
|
XtPointer params,
|
|
XtPointer num_params);
|
|
static void InitiateCommandInvokerRequest(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * host,
|
|
char * dir) ;
|
|
static void ProcessCommandInvokerRequest(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir) ;
|
|
static Boolean ResolveCommandInvokerMessagePieces(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir) ;
|
|
static Tt_callback_action _DbReloadCB(Tt_message m, Tt_pattern p);
|
|
static void _DtActTimerCB( XtPointer clientData,
|
|
XtIntervalId timerId);
|
|
static void _DtActIndicatorCB( XtPointer clientData,
|
|
XtIntervalId timerId);
|
|
|
|
/******** End Static Function Declarations ********/
|
|
|
|
|
|
/* Pointers to localizable strings */
|
|
static String PromptDialogTitle;
|
|
static String ErrorPostfix;
|
|
static String PromptDialogLabel;
|
|
static String ContinueMessage;
|
|
static String HostErrorMsg;
|
|
static String HostErrorMsg2;
|
|
static String NoActionMsg;
|
|
static String NoActionMsg2;
|
|
static String NoActionMsg3;
|
|
static String MapErrorMsg;
|
|
static String InvalidFileMsg;
|
|
static String MultiHostErrorMsg;
|
|
static String IcccmReqErrorMsg;
|
|
static String NoToolTalkConnMsg;
|
|
static String UnSupportedObjMsg;
|
|
static String TmpFileCreateErrorMsg;
|
|
static String TmpFileOpenErrorMsg;
|
|
static String TmpFileWriteErrorMsg;
|
|
|
|
/*
|
|
* RWV:
|
|
* These error messages are used in the ActionTt.c file
|
|
* but were declared static to this file. -- For the
|
|
* time being I made them global to get things to work.
|
|
*/
|
|
String ToolTalkErrorMsg;
|
|
String ToolTalkErrorMsg2;
|
|
String TtFileArgMapErr;
|
|
|
|
|
|
/*
|
|
* Variables needed to make the "Escape" key remove the prompt dialog.
|
|
*/
|
|
static XtActionsRec actionTable [] = {
|
|
{"Escape", (XtActionProc) CancelOut},
|
|
};
|
|
static char translations_escape[] = "<Key>osfCancel:Escape()";
|
|
|
|
|
|
/* Help files */
|
|
#define PROMPT_HELP "vg_act"
|
|
|
|
|
|
/* Maximum Indicator activation duration (in milliseconds) */
|
|
|
|
#define INDICATOR_TIME (120 * 1000)
|
|
#define MIN_INDICATOR_TIME (5 * 1000)
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
******************************************************************************
|
|
*
|
|
* Public API Functions
|
|
*
|
|
******************************************************************************
|
|
*****************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* DtActionInvoke -- invoke an action
|
|
* Widget w; ( widget for UI needs)
|
|
* char *action; ( action name )
|
|
* int aac; ( action arg count )
|
|
* ActionArgp aap; ( action argument pointer )
|
|
* char *termOpts; ( (opt) terminal options)
|
|
* char *execHost; ( (opt) execution host )
|
|
* char *cwd; ( (opt) cwd for this action )
|
|
* int useIndicator; ( 1 ==> use indicator, 0 ==> not )
|
|
* DtActionCallbackProc statusUpdateCb; (user supplied fcn)
|
|
* XtPointer client_data (user supplied client data)
|
|
*****************************************************************************/
|
|
DtActionInvocationID
|
|
DtActionInvoke(
|
|
Widget w,
|
|
char *action,
|
|
DtActionArg *aap,
|
|
int aac,
|
|
char *termOpts,
|
|
char *execHost,
|
|
char *cwd,
|
|
int useIndicator,
|
|
DtActionCallbackProc statusUpdateCb,
|
|
XtPointer client_data)
|
|
{
|
|
register int i;
|
|
ActionRequest *request;
|
|
char *contextHost= NULL;/* dummy to replace old parameter */
|
|
_DtActInvRecT *invp; /* pointer to invocation record */
|
|
Tt_status status = TT_OK;
|
|
static Boolean initialized = False;
|
|
extern XtAppContext *_DtInitAppContextp;
|
|
_DtSvcWidgetToAppContext(w);
|
|
|
|
_DtSvcAppLock(app);
|
|
_DtSvcAppLock(*_DtInitAppContextp);
|
|
|
|
/* We can't handle gadgets; use the parent, if necessary */
|
|
if (XmIsGadget(w))
|
|
w = XtParent(w);
|
|
|
|
_DtSvcProcessLock();
|
|
if ( !initialized )
|
|
{
|
|
mode_t mode;
|
|
char *tmpDir;
|
|
|
|
InitLocalizedStrings();
|
|
|
|
/*
|
|
* Make sure Tooltalk is initialized
|
|
*/
|
|
status = _DtInitializeToolTalk(w);
|
|
if (TT_OK != status)
|
|
{
|
|
NoToolTalkConnectionError(w, action, status);
|
|
_DtSvcProcessUnlock();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Create the DtTmp directory, if necessary.
|
|
*/
|
|
tmpDir = _DtGetDtTmpDir();
|
|
/* mode == 0755 */
|
|
mode = (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH );
|
|
if ( mkdir(tmpDir,mode) )
|
|
{
|
|
/*
|
|
* Tmp directory creation failure.
|
|
*
|
|
* Make one attempt to create the parent directory if the error
|
|
* was because of a missing path component -- It may be because
|
|
* the "$HOME/.dt" directory hasn't yet been created.
|
|
*/
|
|
if ( errno == ENOENT )
|
|
{
|
|
char *parentDir = _DtDirname(tmpDir);
|
|
if (parentDir && 0 == mkdir(parentDir,mode))
|
|
mkdir(tmpDir,mode);
|
|
if (parentDir) XtFree(parentDir);
|
|
}
|
|
}
|
|
XtFree(tmpDir);
|
|
|
|
_DtInitializeCommandInvoker( XtDisplay(w),
|
|
NULL, /* bms tool class -- ignored */
|
|
NULL, /* application class -- ignored */
|
|
(DtSvcMsgContext)NULL, /* reloadDBHandler -- none here */
|
|
(_DtInitAppContextp ?
|
|
*_DtInitAppContextp : XtWidgetToApplicationContext(w)));
|
|
}
|
|
initialized = True;
|
|
_DtSvcProcessUnlock();
|
|
|
|
/* Start the activity indicator */
|
|
if ( useIndicator ) {
|
|
_DtSendActivityNotification(INDICATOR_TIME);
|
|
}
|
|
|
|
if ( (invp = CreateInvocationRecord(action,w,aap,aac)) == NULL)
|
|
{
|
|
myassert( 0 ); /* no request structure --should never happen */
|
|
/* give up -- cannot allocate record */
|
|
|
|
if ( useIndicator ) _DtSendActivityDoneNotification();
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
_DtSvcAppUnlock(app);
|
|
return 0;
|
|
}
|
|
|
|
myassert(invp->id);
|
|
|
|
if ( useIndicator ) {
|
|
/* Start timer for minimum blink time */
|
|
XtAppAddTimeOut(XtWidgetToApplicationContext(w),
|
|
MIN_INDICATOR_TIME,
|
|
(XtTimerCallbackProc) _DtActIndicatorCB,
|
|
(XtPointer) invp->id );
|
|
}
|
|
|
|
/*
|
|
* Add user callback info to the new invocation record.
|
|
*/
|
|
invp->client_data = client_data;
|
|
invp->cb = statusUpdateCb;
|
|
|
|
/* Create and fill in the request structure */
|
|
if ( !IS_INV_FINISHED(invp->state) && (request = CreateActionRequest (
|
|
w,action,aap,aac,termOpts,execHost,contextHost,cwd,invp)) != NULL)
|
|
{
|
|
if (ProcessRequest(w, request))
|
|
{
|
|
/* all done invoking ? */
|
|
RESET_INV_PENDING(invp->state);
|
|
|
|
/* We should only get here if all requests have been honored */
|
|
SET_INV_COMPLETE(invp->state);
|
|
|
|
/*
|
|
* Evaluate whether we are done with this invocation.
|
|
* We may have to return values to the caller.
|
|
*/
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
_DtFreeRequest(request);
|
|
}
|
|
/* Otherwise, a dialog was posted; request will be freed later */
|
|
}
|
|
|
|
/*
|
|
* Set the indicator that the invocation Id has been returned
|
|
* add a timer so that we can return status info once the
|
|
* caller has gotten the invocation Id.
|
|
*/
|
|
SET_INV_ID_RETURNED(invp->state);
|
|
XtAppAddTimeOut(XtWidgetToApplicationContext(w),
|
|
0 /* call back immediately */,
|
|
(XtTimerCallbackProc) _DtActTimerCB,
|
|
(XtPointer) invp->id );
|
|
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
_DtSvcAppUnlock(app);
|
|
return invp->id;
|
|
}
|
|
|
|
|
|
void
|
|
DtDbReloadNotify( DtDbReloadCallbackProc proc, XtPointer client_data)
|
|
{
|
|
Tt_status status;
|
|
Tt_pattern pattern;
|
|
char * sessId;
|
|
extern XtAppContext *_DtInitAppContextp;
|
|
|
|
|
|
if (NULL == proc) return;
|
|
|
|
_DtSvcAppLock(*_DtInitAppContextp);
|
|
|
|
/*
|
|
* Check if we need to initialize tooltalk
|
|
*/
|
|
status = _DtInitializeToolTalk(NULL);
|
|
if (TT_OK != status) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This function register a ToolTalk pattern for every
|
|
* callback added.
|
|
*/
|
|
pattern = tt_pattern_create();
|
|
if (tt_ptr_error(pattern) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
|
|
if (tt_pattern_scope_add(pattern, TT_SESSION) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
if (tt_pattern_category_set(pattern, TT_OBSERVE) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
if (tt_pattern_class_add(pattern, TT_NOTICE) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
if (tt_pattern_state_add(pattern, TT_SENT) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
sessId = tt_default_session();
|
|
if (tt_pattern_session_add(pattern, sessId) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
tt_free( sessId );
|
|
if (tt_pattern_op_add(pattern, "DtTypes_Reloaded") != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Store information needed by the callback in the user data
|
|
* fields of the pattern.
|
|
*/
|
|
status = tt_pattern_user_set(pattern, 0, (void *)proc);
|
|
if (status != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
status = tt_pattern_user_set(pattern, 1, (void *)client_data);
|
|
if (status != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* _DbReloadCB is the ToolTalk callback which will call
|
|
* the user callback.
|
|
*/
|
|
if (tt_pattern_callback_add(pattern, _DbReloadCB) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
|
|
if (tt_pattern_register(pattern) != TT_OK) {
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
return;
|
|
}
|
|
|
|
_DtSvcAppUnlock(*_DtInitAppContextp);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
******************************************************************************
|
|
*
|
|
* Private API Functions
|
|
*
|
|
******************************************************************************
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
_DtActTimerCB( XtPointer clientData, XtIntervalId IntId)
|
|
{
|
|
_DtActExecutionLeafNodeCleanup((unsigned long)clientData,
|
|
NULL,0,True);
|
|
}
|
|
|
|
static void
|
|
_DtActIndicatorCB( XtPointer clientData, XtIntervalId IntId )
|
|
{
|
|
unsigned long invocId = (unsigned long) clientData;
|
|
_DtActInvRecT *invRecP = _DtActFindInvRec( invocId );
|
|
|
|
if ( !invRecP || IS_INV_FINISHED(invRecP->state) )
|
|
{
|
|
/* Turn off the activity indicator */
|
|
_DtSendActivityDoneNotification();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Let the action turn off the indicator when invocation
|
|
* is complete.
|
|
*/
|
|
SET_INV_INDICATOR_ON(invRecP->state);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Routines and static data to support DtDbReloadNotify which supplies
|
|
* the user with transparent access to the messaging system for
|
|
* notification of action/datatypes database changes.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* _DbReloadCB
|
|
* A ToolTalk callback function used to map callback arguments to
|
|
* the callback function specified by the user. This function invokes the
|
|
* user-defined DtReloadNotifyProc callback with the desired client_data.
|
|
*/
|
|
|
|
static Tt_callback_action
|
|
_DbReloadCB(Tt_message m, Tt_pattern p)
|
|
{
|
|
DtDbReloadCallbackProc proc;
|
|
XtPointer client_data;
|
|
|
|
/*
|
|
* user data 0: DtDbReloadCallbackProc proc;
|
|
* user data 1: XtPointer client_data;
|
|
*/
|
|
proc = (DtDbReloadCallbackProc)tt_pattern_user(p, 0);
|
|
client_data = (XtPointer)tt_pattern_user(p, 1);
|
|
|
|
/*
|
|
* Call registered callback function.
|
|
*/
|
|
if (proc) (*proc)(client_data);
|
|
|
|
return TT_CALLBACK_PROCESSED;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Error Dialog Code */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* 'Ok' callback for the generic error dialogs. It will simply destroy
|
|
* the dialog.
|
|
*/
|
|
|
|
static void
|
|
FreeErrorDialog(
|
|
Widget w,
|
|
XtPointer user_data,
|
|
XtPointer call_data )
|
|
|
|
{
|
|
XtDestroyWidget(XtParent(w));
|
|
}
|
|
|
|
|
|
/*
|
|
* Generic function used to create an error dialog.
|
|
*/
|
|
|
|
void
|
|
_DtCreateErrorDialog(
|
|
Widget w,
|
|
String actionName,
|
|
XmString msg )
|
|
|
|
{
|
|
String title;
|
|
int n;
|
|
Arg args[10];
|
|
Widget dialog;
|
|
XmString ok;
|
|
XWindowAttributes xwa;
|
|
Status status;
|
|
Boolean is_mapped = False;
|
|
char *fmt;
|
|
|
|
fmt = XtNewString((char *)Dt11GETMESSAGE(2, 1, "%1$s%2$s%3$s"));
|
|
|
|
/* Create the title string for the dialog */
|
|
title = (char *)XtMalloc((Cardinal)(strlen(PromptDialogTitle) +
|
|
strlen(actionName) +
|
|
strlen(ErrorPostfix) +
|
|
strlen(fmt) + 1));
|
|
|
|
(void)sprintf(title, fmt, PromptDialogTitle, actionName, ErrorPostfix);
|
|
|
|
XtFree(fmt);
|
|
|
|
ok = XmStringCreateLocalized((String)_DtOkString);
|
|
|
|
if (XtIsRealized(w))
|
|
{
|
|
status = XGetWindowAttributes (XtDisplay (w), XtWindow (w), &xwa);
|
|
if (status && (xwa.map_state == IsViewable))
|
|
is_mapped = True;
|
|
}
|
|
|
|
/* Create the error dialog */
|
|
n = 0;
|
|
XtSetArg(args[n], XmNmessageString, msg); n++;
|
|
XtSetArg(args[n], XmNtitle, title); n++;
|
|
XtSetArg(args[n], XmNokLabelString, ok); n++;
|
|
XtSetArg(args[n], XmNuseAsyncGeometry, True); n++;
|
|
if (!is_mapped)
|
|
{
|
|
XtSetArg (args[n], XmNdefaultPosition, False);
|
|
n++;
|
|
}
|
|
dialog = XmCreateErrorDialog(w, "errorDialog", args, n);
|
|
XmStringFree(ok);
|
|
XtFree(title);
|
|
|
|
/* Set up callbacks */
|
|
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
|
|
XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
|
|
XtAddCallback(dialog, XmNokCallback, FreeErrorDialog,
|
|
(XtPointer)NULL);
|
|
|
|
/*
|
|
* If the widget is not mapped, center this dialog.
|
|
*/
|
|
if (!is_mapped)
|
|
{
|
|
Dimension dialogWd, dialogHt;
|
|
Widget dialogShell = XtParent(dialog);
|
|
|
|
XtSetArg(args[0], XmNmappedWhenManaged, False);
|
|
XtSetValues(dialogShell, args, 1);
|
|
|
|
XtManageChild(dialog);
|
|
XtRealizeWidget (dialogShell);
|
|
|
|
XtSetArg(args[0], XmNwidth, &dialogWd);
|
|
XtSetArg(args[1], XmNheight, &dialogHt);
|
|
XtGetValues(dialog, args, 2);
|
|
|
|
XtSetArg (args[0], XmNx,
|
|
(WidthOfScreen(XtScreen(dialog)) - dialogWd) / 2U);
|
|
XtSetArg (args[1], XmNy,
|
|
(HeightOfScreen(XtScreen(dialog)) - dialogHt) / 2U);
|
|
XtSetValues (dialog, args, 2);
|
|
|
|
XtSetArg(args[0], XmNmappedWhenManaged, True);
|
|
XtSetValues(dialogShell, args, 1);
|
|
}
|
|
|
|
/* Display the dialog */
|
|
XtManageChild(dialog);
|
|
}
|
|
|
|
|
|
/*
|
|
* Error handler for when the user supplied a file name which cannot
|
|
* be accessed. Displays an error dialog. Most often, this is caused
|
|
* when a filename with an embedded space is received in the object list.
|
|
*/
|
|
|
|
static void
|
|
InvalidFilename(
|
|
Widget w,
|
|
String actionName,
|
|
String filename )
|
|
|
|
{
|
|
XmString pt1, pt2, msg;
|
|
|
|
/* Construct the error message */
|
|
pt1 = XmStringCreateLocalized(InvalidFileMsg);
|
|
pt2 = XmStringCreateLocalized(filename);
|
|
msg = XmStringConcat(pt1, pt2);
|
|
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(pt1);
|
|
XmStringFree(pt2);
|
|
XmStringFree(msg);
|
|
}
|
|
|
|
|
|
/*
|
|
* Error handler for when the user supplied a host name which cannot
|
|
* be accessed. Displays an error dialog.
|
|
*/
|
|
|
|
static void
|
|
HostAccessError(
|
|
Widget w,
|
|
String actionName,
|
|
String hostName )
|
|
|
|
{
|
|
XmString pt1, pt2, pt3, msg, msg2;
|
|
|
|
/* Construct the error message */
|
|
pt1 = XmStringCreateLocalized(HostErrorMsg);
|
|
pt2 = XmStringCreateLocalized(hostName);
|
|
pt3 = XmStringCreateLocalized(HostErrorMsg2);
|
|
|
|
msg = XmStringConcat(pt1, pt2);
|
|
msg2 = XmStringConcat(msg, pt3);
|
|
|
|
_DtCreateErrorDialog(w, actionName, msg2);
|
|
|
|
XmStringFree(pt1);
|
|
XmStringFree(pt2);
|
|
XmStringFree(pt3);
|
|
XmStringFree(msg);
|
|
XmStringFree(msg2);
|
|
}
|
|
|
|
|
|
/*
|
|
* Error handler for when the user supplied a collection of host names
|
|
* which cannot be accessed. Displays an error dialog.
|
|
*/
|
|
|
|
static void
|
|
MultiHostAccessError(
|
|
Widget w,
|
|
String actionName,
|
|
String hostList )
|
|
|
|
{
|
|
XmString msg;
|
|
char * buf = XtMalloc(strlen(MultiHostErrorMsg) + strlen(hostList) + 10);
|
|
|
|
sprintf(buf, MultiHostErrorMsg, hostList);
|
|
msg = XmStringCreateLocalized(buf);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
|
|
XmStringFree(msg);
|
|
XtFree(buf);
|
|
}
|
|
|
|
/***************************************************************************
|
|
*
|
|
* MapError - this function creates an error message when an action
|
|
* cannot be executed because of too many "MAPs".
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* Widget w; - Widget needed for posting the error dialog.
|
|
*
|
|
* String actionName; - The name of the action.
|
|
*
|
|
* RETURN: void
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void
|
|
MapError(
|
|
Widget w,
|
|
String actionName )
|
|
{
|
|
|
|
XmString msg = XmStringCreateLocalized(MapErrorMsg);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
|
|
}
|
|
|
|
/*
|
|
* Error handler for when an action definition cannot be found to
|
|
* match an object of a particular type. Displays an error dialog.
|
|
* A different message is displayed when no objects is supplied.
|
|
*/
|
|
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: Will need to also handle a string or a buffer ... eventually */
|
|
|
|
static void
|
|
NoActionError(
|
|
Widget w,
|
|
DtShmBoson origNameQuark,
|
|
char * actionName,
|
|
char * type,
|
|
char * host,
|
|
char * dir,
|
|
char * file )
|
|
|
|
{
|
|
char *msgbuf = XtMalloc(2*MAXPATHLEN);
|
|
XmString msg;
|
|
char * name = NULL;
|
|
|
|
/* Construct the error message */
|
|
if ((host == NULL) && (dir == NULL) && (file == NULL) && (type == NULL) )
|
|
{
|
|
(void)sprintf(msgbuf,NoActionMsg2,actionName);
|
|
}
|
|
else if ( (type != NULL ) && (file == NULL) && (dir == NULL))
|
|
{
|
|
/*
|
|
* We are dealing with a buffer object for which an action couldn't
|
|
* be located.
|
|
*/
|
|
(void)sprintf(msgbuf,NoActionMsg3,actionName,type);
|
|
}
|
|
else
|
|
{
|
|
name = (char *)XtMalloc((Cardinal)((host ? strlen(host) : 0) +
|
|
(dir ? strlen(dir) : 0) +
|
|
(file ? strlen(file) : 0) + 10));
|
|
name[0] = '\0';
|
|
|
|
/* Construct the file name */
|
|
if (host)
|
|
{
|
|
(void)strcat(name, host);
|
|
(void)strcat(name, ":");
|
|
}
|
|
|
|
if (dir)
|
|
{
|
|
(void)strcat(name, dir);
|
|
if (strcmp(dir, "/") != 0)
|
|
(void)strcat(name, "/");
|
|
}
|
|
|
|
if (file)
|
|
(void)strcat(name, file);
|
|
|
|
(void)sprintf(msgbuf,NoActionMsg,actionName,name,type);
|
|
|
|
}
|
|
msg = XmStringCreateLocalized(msgbuf);
|
|
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
if (msgbuf) XtFree(msgbuf);
|
|
XtFree(name);
|
|
}
|
|
|
|
|
|
/*
|
|
* Error handler for when the Command Invoker detects an error, and
|
|
* send us a failure response to our action request.
|
|
* Display an error dialog.
|
|
*/
|
|
|
|
static void
|
|
CommandInvokerError(
|
|
Widget w,
|
|
String actionName,
|
|
String errorString )
|
|
|
|
{
|
|
XmString msg;
|
|
|
|
msg = XmStringCreateLocalized(errorString);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
}
|
|
|
|
|
|
/*
|
|
* If an action requires a ToolTalk connection, and we were unable to get
|
|
* one, then we will fail and post an error dialog.
|
|
*/
|
|
|
|
static void
|
|
NoToolTalkConnectionError(
|
|
Widget w,
|
|
String actionName,
|
|
Tt_status status)
|
|
|
|
{
|
|
XmString msg;
|
|
char *errmsg, *statmsg;
|
|
|
|
if (TT_OK == status)
|
|
statmsg = "";
|
|
else
|
|
statmsg = tt_status_message(status);
|
|
errmsg = XtMalloc(strlen(NoToolTalkConnMsg) + strlen(statmsg) + 2);
|
|
sprintf(errmsg, NoToolTalkConnMsg, statmsg);
|
|
|
|
msg = XmStringCreateLocalized(errmsg);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
|
|
XtFree(errmsg);
|
|
XmStringFree(msg);
|
|
}
|
|
|
|
static void
|
|
TmpFileCreateError( Widget w, char *actionName, char *dirName)
|
|
{
|
|
XmString msg;
|
|
char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
|
|
|
|
sprintf(msgbuf,TmpFileCreateErrorMsg,_DtActNULL_GUARD(dirName),
|
|
actionName);
|
|
|
|
msg = XmStringCreateLocalized(msgbuf);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
if (msgbuf) XtFree(msgbuf);
|
|
}
|
|
|
|
static void
|
|
TmpFileOpenError( Widget w, char *actionName, char *fileName)
|
|
{
|
|
XmString msg;
|
|
char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
|
|
|
|
sprintf(msgbuf,TmpFileOpenErrorMsg,_DtActNULL_GUARD(fileName),
|
|
actionName);
|
|
|
|
msg = XmStringCreateLocalized(msgbuf);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
if (msgbuf) XtFree(msgbuf);
|
|
}
|
|
|
|
static void
|
|
TmpFileWriteError( Widget w, char *actionName, char *fileName)
|
|
{
|
|
XmString msg;
|
|
char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
|
|
|
|
sprintf(msgbuf,TmpFileWriteErrorMsg,_DtActNULL_GUARD(fileName),
|
|
actionName);
|
|
|
|
msg = XmStringCreateLocalized(msgbuf);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
if (msgbuf) XtFree(msgbuf);
|
|
}
|
|
|
|
static void
|
|
UnSupportedObject( Widget w, char *actionName, int objClass)
|
|
{
|
|
XmString msg;
|
|
char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
|
|
|
|
sprintf(msgbuf,UnSupportedObjMsg,objClass,actionName);
|
|
|
|
msg = XmStringCreateLocalized(msgbuf);
|
|
_DtCreateErrorDialog(w, actionName, msg);
|
|
XmStringFree(msg);
|
|
if (msgbuf) XtFree(msgbuf);
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Main Work Functions For _DtActionInvoke() */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
|
|
/*
|
|
* Load the globals pointing to any localizable strings.
|
|
*/
|
|
|
|
static void
|
|
InitLocalizedStrings( void )
|
|
|
|
{
|
|
PromptDialogTitle = XtNewString(((char *)Dt11GETMESSAGE(2, 3, "Action: ")));
|
|
ErrorPostfix = XtNewString(((char *)Dt11GETMESSAGE(2, 4, " [Error]")));
|
|
PromptDialogLabel = XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 5, "Please enter the following information:")));
|
|
ContinueMessage = XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 6, "You have supplied more parameters than the selected action requires.\n\nSelect 'Ok' to ignore extra parameters.\n\nSelect 'Cancel' to terminate the action.")));
|
|
HostErrorMsg =XtNewString(((char *)Dt11GETMESSAGE(2, 7, "The following host was not accessible:\n\n ")));
|
|
|
|
#ifdef _SUN_OS
|
|
HostErrorMsg2 =XtNewString(((char *)Dt11GETMESSAGE(2, 8, "\n\nThis may be because the remote host's file\nsystem is not properly mounted.\n\n")));
|
|
#else
|
|
HostErrorMsg2 =XtNewString(((char *)Dt11GETMESSAGE(2, 8, "\n\nCheck that the appropriate remote data access connection\nhas been made.\n\n(See \"The Common Desktop Environment User's Guide\"\nfor more information.)\n")));
|
|
#endif
|
|
NoActionMsg =XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 9, "Either action \"%s\" was not found\n or\nthis action does not apply to the file:\n \"%s\"\nwith data attribute: \"%s%\"\n\n")));
|
|
NoActionMsg2 = XtNewString(((char *)Dt11GETMESSAGE(2, 10, "Action \"%s\" was not found.\n")));
|
|
InvalidFileMsg = XtNewString(((char *)Dt11GETMESSAGE(2, 11, "The following file was not found:\n\n ")));
|
|
MapErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2, 12, "This action cannot be executed because it contains too\nmany levels of MAPs, or the mapping is \"circular\".")));
|
|
MultiHostErrorMsg =XtNewString(((char *)Dt11GETMESSAGE(2,13, "Unable to invoke the requested action.\n\nAre the following hosts accessible?\n (%s)\nDoes the corresponding program exist?\n(Run " CDE_INSTALLATION_TOP "/bin/dttypes to match actions and programs.)\n\nHas your system run out of room to execute new processes?")));
|
|
IcccmReqErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2,14,"The request to service this action has failed")));
|
|
NoToolTalkConnMsg = XtNewString(((char *)Dt11GETMESSAGE(2,15,"The request to service this action has failed.\nA ToolTalk connection could not be established:\n\n%s")));
|
|
ToolTalkErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2,16, "The request to service this action has failed")));
|
|
ToolTalkErrorMsg2 = XtNewString(((char *)Dt11GETMESSAGE(2,17, "The request to service this action has failed for the following reason:\n\n %s")));
|
|
TtFileArgMapErr = XtNewString((char *)Dt11GETMESSAGE(2,18,"An error occurred while attempting to map one of\nthe file arguments."));
|
|
NoActionMsg3 =XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 19, "Either action \"%s\" was not found\n or\nthis action does not apply to buffers of type:\n \"%s\"\n\n")));
|
|
UnSupportedObjMsg = XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 21, "Unsupported input object class: \"%d\"\nfor action: \"%s\".")));
|
|
TmpFileCreateErrorMsg = XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 22, "Unable to create a temporary file in directory: \"%s\"\nfor the action named: \"%s\"")));
|
|
TmpFileOpenErrorMsg = XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 23, "Unable to open a temporary file: \"%s\"\nfor the action named: \"%s\"")));
|
|
TmpFileWriteErrorMsg = XtNewString(
|
|
((char *)Dt11GETMESSAGE(2, 24, "Unable to write a temporary file: \"%s\"\nfor the action named: \"%s\"")));
|
|
}
|
|
|
|
/*
|
|
* This function takes the information supplied by the caller of
|
|
* _DtActionInvoke(), and turns it into an internal format. This
|
|
* includes parsing out each of the file names, and converting the
|
|
* type from a string to an integer.
|
|
*
|
|
* The structure returned must be freed up eventually.
|
|
*/
|
|
|
|
static ActionRequest *
|
|
CreateActionRequest(
|
|
Widget w,
|
|
String actionName,
|
|
DtActionArg *aap,
|
|
int aac,
|
|
String termOpts,
|
|
String execHost,
|
|
String cwdHost,
|
|
String cwdDir,
|
|
_DtActInvRecT *invp )
|
|
|
|
{
|
|
register int i, j;
|
|
int numObjects = 0;
|
|
ObjectData * objectDataArray;
|
|
ObjectData objectData;
|
|
register ActionRequest * request;
|
|
|
|
|
|
/* Allocate a new request structure -- zero filled */
|
|
request = (ActionRequest *) XtCalloc(1,(Cardinal)sizeof(ActionRequest));
|
|
|
|
request->actionName = XtNewString(actionName);
|
|
|
|
if (termOpts)
|
|
request->termOpts = XtNewString(termOpts);
|
|
|
|
if (execHost)
|
|
request->execHost = XtNewString(execHost);
|
|
|
|
if (cwdHost)
|
|
request->cwdHost = XtNewString(cwdHost);
|
|
|
|
if (cwdDir)
|
|
request->cwdDir = XtNewString(cwdDir);
|
|
|
|
request->objsUsed = -1; /* -1 => not yet determined */
|
|
|
|
if ( invp )
|
|
request->invocId = invp->id;
|
|
|
|
/* If there are no objects, then there's no reason to continue */
|
|
if ((aac <= 0) || (aap == NULL))
|
|
return(request);
|
|
|
|
/*
|
|
* Allocate space for all the object data at once
|
|
*/
|
|
objectDataArray = (ObjectData *) XtCalloc(aac,(sizeof(ObjectData)));
|
|
|
|
/*
|
|
* process object names -- assume all file names are of the form
|
|
* /path/file (do NOT allow host:/path/file)
|
|
*/
|
|
for ( i = 0; i < aac ; i++ )
|
|
{
|
|
memset((void *)&objectData,0,sizeof(ObjectData));
|
|
if ( (aap+i)->argClass == DtACTION_FILE )
|
|
{
|
|
if (ParseFileArgument(w, request, &objectData, NULL ,
|
|
aap[i].u.file.name, NULL , True))
|
|
{
|
|
XtFree((char *)objectDataArray);
|
|
return(NULL);
|
|
}
|
|
}
|
|
else if ( (aap+i)->argClass == DtACTION_BUFFER )
|
|
{
|
|
/*
|
|
* Check if we've already created a tmp file for this buffer
|
|
* if so fill in the request structure as if this were a file
|
|
* object.
|
|
*/
|
|
if ( invp->info[i].name )
|
|
{
|
|
/*
|
|
* Use the tmp file name and type stored in the invocation rec.
|
|
* The FILE bit will be set in the object mask -- we will also
|
|
* set the BUFFER bit to indicate that this is a tmp file
|
|
* representing a buffer.
|
|
*/
|
|
if (ParseFileArgument(w, request, &objectData, NULL ,
|
|
invp->info[i].name, invp->info[i].type , True))
|
|
{
|
|
XtFree((char *)objectDataArray);
|
|
return(NULL);
|
|
}
|
|
/*
|
|
* Set the buffer object bit as well -- and check whether
|
|
* this buffer is intended to be writable, if not reset the
|
|
* writable bit set in ParseFileArgument().
|
|
*/
|
|
SET_BUFFER_OBJ(objectData.mask);
|
|
if ( !(aap[i].u.buffer.writable) )
|
|
RESET_WRITE_OBJ(objectData.mask);
|
|
/*
|
|
* Save the buffer type info if we have it
|
|
*/
|
|
if ( aap[i].u.buffer.type )
|
|
objectData.type = _DtDtsMMStringToBoson(aap[i].u.buffer.type);
|
|
|
|
/*
|
|
* Save the original buffer pointer and size for this
|
|
* pseudo-file.
|
|
*/
|
|
if ( aap[i].u.buffer.bp )
|
|
{
|
|
objectData.u.file.bp = aap[i].u.buffer.bp;
|
|
objectData.u.file.sizebp = aap[i].u.buffer.size;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Do buffer stuff here */
|
|
SET_BUFFER_OBJ(objectData.mask);
|
|
if ( aap[i].u.buffer.writable )
|
|
SET_WRITE_OBJ(objectData.mask);
|
|
|
|
/*
|
|
* If the buffer type has been passed in to us save its quark
|
|
* in the object structure now. When/if the type is determined
|
|
* later this object record should be filled in with the
|
|
* necessary quark.
|
|
*/
|
|
if ( aap[i].u.buffer.type )
|
|
objectData.type = _DtDtsMMStringToBoson(aap[i].u.buffer.type);
|
|
else
|
|
{
|
|
/*
|
|
* We have already determined the buffer type when creating
|
|
* the invocation record. So get the type string
|
|
* from the invocation record.
|
|
*/
|
|
myassert(invp->info[i].type);
|
|
if (invp->info[i].type)
|
|
{
|
|
objectData.type = _DtDtsMMStringToBoson(invp->info[i].type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Save buffer contents
|
|
*/
|
|
if ( aap[i].u.buffer.bp )
|
|
{
|
|
objectData.u.buffer.size = aap[i].u.buffer.size;
|
|
objectData.u.buffer.bp = aap[i].u.buffer.bp;
|
|
} else
|
|
{
|
|
myassert(0 /* null buffer pointer */ );
|
|
objectData.u.buffer.bp = NULL;
|
|
objectData.u.buffer.size = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* structure assignment */
|
|
objectDataArray[i] = objectData;
|
|
numObjects++;
|
|
}
|
|
|
|
request->numObjects = numObjects;
|
|
request->objects = objectDataArray;
|
|
|
|
return(request);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
*
|
|
* static _DtActInvRecT *
|
|
* CreateInvocationRecord(actionName,w,aap,aac)
|
|
* Create an invocation record and fill in argument information
|
|
* return a pointer to the newly allocated invocation record.
|
|
*
|
|
*****************************************************************************/
|
|
static _DtActInvRecT *
|
|
CreateInvocationRecord(
|
|
char *actionName,
|
|
Widget w,
|
|
DtActionArg *aap,
|
|
int aac)
|
|
{
|
|
register int i;
|
|
_DtActInvRecT *invp; /* pointer to invocation record */
|
|
char *tmp;
|
|
|
|
/*
|
|
* allocate invocation record and ID to return to caller
|
|
*/
|
|
invp = _DtActAllocInvRec();
|
|
if ( !invp )
|
|
{
|
|
/*
|
|
* RWV --> ideally we would need error message here
|
|
* but if we are unable to allocate the record we
|
|
* would in all likelyhood be unable to allocate the
|
|
* error message as well. We will essentially assume
|
|
* allocation does not fail as is done throughout the
|
|
* library.
|
|
*/
|
|
return NULL;
|
|
}
|
|
myassert(invp->id != 0);
|
|
|
|
SET_INV_PENDING(invp->state);
|
|
invp->w = w;
|
|
invp->numChildren = 0;
|
|
invp->childRec = NULL;
|
|
|
|
/*
|
|
* Fill in argument information
|
|
*/
|
|
invp->ac = aac;
|
|
|
|
if (aac == 0) {
|
|
invp->info = NULL;
|
|
|
|
return invp;
|
|
}
|
|
|
|
invp->info = (_DtActArgInfo *)XtCalloc(aac,sizeof(_DtActArgInfo));
|
|
|
|
for ( i=0; i < aac; i++ )
|
|
{
|
|
if ( aap[i].argClass == DtACTION_BUFFER )
|
|
{
|
|
int fd; /* tmp file descriptor */
|
|
char *format; /* name template (printf format) */
|
|
char *is_executable; /* IS_EXECUTABLE attribute */
|
|
mode_t mode;
|
|
int bytesToWrite, bytesWritten;
|
|
int closeAttempts;
|
|
|
|
SET_BUFFER_OBJ(invp->info[i].mask);
|
|
if ( aap[i].u.buffer.writable )
|
|
SET_WRITE_OBJ(invp->info[i].mask);
|
|
|
|
/* save original buffer size */
|
|
invp->info[i].size = aap[i].u.buffer.size;
|
|
|
|
/*
|
|
* Determine the type of the buffer object.
|
|
* Typing based on the object name takes precedence
|
|
* over the type "hint".
|
|
*/
|
|
if ( aap[i].u.buffer.name
|
|
|| (aap[i].u.buffer.type == NULL) )
|
|
{
|
|
tmp = DtDtsBufferToDataType(
|
|
aap[i].u.buffer.bp,aap[i].u.buffer.size,
|
|
aap[i].u.buffer.name);
|
|
/*
|
|
* Malloc our own copy of the type string so we won't
|
|
* have to worry about when to call DtDtsFreeDataType() later.
|
|
*/
|
|
invp->info[i].type = XtNewString(tmp);
|
|
DtDtsFreeDataType(tmp);
|
|
} else
|
|
{
|
|
invp->info[i].type = XtNewString(aap[i].u.buffer.type);
|
|
}
|
|
|
|
/*
|
|
* Simply create tmp files for ALL buffers.
|
|
*
|
|
* This allows us to work around problems related to client
|
|
* programs making subsequent changes to or freeing the memory
|
|
* associated with the buffer before we are through with it.
|
|
*
|
|
* For actions of type CMD we need to have files anyway.
|
|
*
|
|
* Be sure to create tmp files with
|
|
* a suffix proper for the buffer type.
|
|
*/
|
|
|
|
/* first determine the permissions for the new tmp file */
|
|
is_executable =
|
|
DtDtsDataTypeToAttributeValue(invp->info[i].type,
|
|
_DtActIS_EXECUTABLE,NULL);
|
|
/*
|
|
* The tmp file should at LEAST be readable
|
|
*/
|
|
mode=( S_IRUSR | S_IRGRP | S_IROTH );
|
|
if ( aap[i].u.buffer.writable )
|
|
mode |= ( S_IWUSR | S_IWGRP | S_IWOTH );
|
|
if ( is_executable
|
|
&& DtDtsIsTrue(is_executable) )
|
|
mode |= ( S_IXUSR | S_IXGRP | S_IXOTH );
|
|
|
|
DtDtsFreeAttributeValue(is_executable);
|
|
|
|
|
|
if ( aap[i].u.buffer.name )
|
|
{
|
|
/*
|
|
* Attempt to use the name supplied for the buffer.
|
|
*/
|
|
invp->info[i].name = _DtActGenerateTmpFile(NULL,
|
|
aap[i].u.buffer.name,mode,&fd);
|
|
}
|
|
if ( !invp->info[i].name )
|
|
{
|
|
/*
|
|
* Generate tmp file based on format supplied for the
|
|
* file type.
|
|
*/
|
|
format = DtDtsDataTypeToAttributeValue(invp->info[i].type,
|
|
_DtActNAME_TEMPLATE,NULL);
|
|
|
|
invp->info[i].name = _DtActGenerateTmpFile(NULL,format,mode,&fd);
|
|
DtDtsFreeAttributeValue(format);
|
|
}
|
|
if ( !invp->info[i].name )
|
|
{
|
|
/*
|
|
* Unable to generate usable tmp file name.
|
|
*/
|
|
/*
|
|
* Error message makes assertion message redundant.
|
|
* myassert(invp->info[i].name);
|
|
*/
|
|
TmpFileCreateError(w,actionName,_DtGetDtTmpDir());
|
|
|
|
RESET_INV_PENDING(invp->state);
|
|
SET_INV_ERROR(invp->state);
|
|
SET_INV_CANCEL(invp->state);
|
|
|
|
return invp;
|
|
}
|
|
|
|
/*
|
|
* Write contents of buffer to temp file
|
|
*/
|
|
myassert( fd >= 0 );
|
|
for ( bytesToWrite = aap[i].u.buffer.size, bytesWritten = 0;
|
|
bytesToWrite > 0;
|
|
bytesToWrite -= bytesWritten)
|
|
{
|
|
bytesWritten = write(fd,aap[i].u.buffer.bp,bytesToWrite);
|
|
if ( bytesWritten < 0 )
|
|
{
|
|
if (errno == EINTR )
|
|
{
|
|
bytesWritten = 0;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
myassert(0 /* Unrecoverable Write Error */);
|
|
TmpFileWriteError(w,actionName,invp->info[i].name);
|
|
|
|
close(fd);
|
|
(void) unlink(invp->info[i].name);
|
|
|
|
RESET_INV_PENDING(invp->state);
|
|
SET_INV_ERROR(invp->state);
|
|
SET_INV_CANCEL(invp->state);
|
|
|
|
return invp;
|
|
}
|
|
}
|
|
}
|
|
|
|
closeAttempts = 0;
|
|
while ( close(fd) )
|
|
{
|
|
/* error closing fd */
|
|
if ( closeAttempts > _DT_ACTION_MAX_CLOSE_TRIES )
|
|
break;
|
|
|
|
switch ( errno )
|
|
{
|
|
case EBADF: /* invalid fd */
|
|
myassert( 0 );
|
|
break;
|
|
case EINTR: /* interrupted sys call */
|
|
closeAttempts++;
|
|
continue; /* try again */
|
|
case ENOSPC: /* Not enough space on NFS-mounted dev */
|
|
TmpFileWriteError(w,actionName,
|
|
invp->info[i].name);
|
|
|
|
unlink(invp->info[i].name);
|
|
RESET_INV_PENDING(invp->state);
|
|
SET_INV_ERROR(invp->state);
|
|
SET_INV_CANCEL(invp->state);
|
|
|
|
/* try another close */
|
|
if ( close(fd) )
|
|
{
|
|
/* It should have worked this time */
|
|
myassert(0);
|
|
}
|
|
return invp;
|
|
default: /* anything else */
|
|
myassert(0);
|
|
break;
|
|
}
|
|
break; /* only try again for conditions with continue */
|
|
}
|
|
|
|
|
|
/*
|
|
* Now that we have created a tmp file for this buffer
|
|
* object set the FILE_OBJ flag as well as the buffer flag.
|
|
* Objects with both the BUFFER and FILE flags set will be
|
|
* recognized as buffers which have been written to tmp files.
|
|
*/
|
|
|
|
SET_FILE_OBJ(invp->info[i].mask);
|
|
|
|
}
|
|
else if ( aap[i].argClass == DtACTION_FILE )
|
|
{
|
|
invp->info[i].name = XtNewString(aap[i].u.file.name);
|
|
|
|
SET_FILE_OBJ(invp->info[i].mask);
|
|
SET_WRITE_OBJ(invp->info[i].mask);
|
|
}
|
|
else
|
|
{
|
|
myassert( 0 /* unsupported object */ );
|
|
UnSupportedObject(w, actionName, aap[i].argClass);
|
|
|
|
RESET_INV_PENDING(invp->state);
|
|
SET_INV_ERROR(invp->state);
|
|
SET_INV_CANCEL(invp->state);
|
|
|
|
return invp;
|
|
}
|
|
}
|
|
|
|
return invp;
|
|
}
|
|
|
|
|
|
|
|
static Boolean
|
|
ParseFileArgument(
|
|
Widget w,
|
|
ActionRequest * request,
|
|
ObjectData * objectData,
|
|
String hostname,
|
|
String filename,
|
|
String filetype,
|
|
Boolean typeFile )
|
|
|
|
{
|
|
register int i, j;
|
|
String dirName;
|
|
String host;
|
|
String dir;
|
|
int hostId;
|
|
char *resolvedPath=NULL;
|
|
|
|
/********************************************************************
|
|
WE NO LONGER ACCEPT host:/path FORMAT
|
|
if (host = _DtHostString(filename))
|
|
{
|
|
hostId = _DtAddEntry(host, &request->hostNames, &request->numHostNames);
|
|
XtFree(host);
|
|
}
|
|
else
|
|
********************************************************************/
|
|
if ( hostname )
|
|
{
|
|
hostId = _DtAddEntry(hostname, &request->hostNames,
|
|
&request->numHostNames);
|
|
}
|
|
else
|
|
{
|
|
if ( request->cwdHost != NULL )
|
|
{
|
|
hostId = _DtAddEntry(request->cwdHost, &request->hostNames,
|
|
&request->numHostNames);
|
|
}
|
|
else
|
|
{
|
|
/* if all else fails use local host */
|
|
host = _DtGetLocalHostName();
|
|
hostId = _DtAddEntry(host, &request->hostNames,
|
|
&request->numHostNames);
|
|
XtFree(host);
|
|
}
|
|
}
|
|
|
|
objectData->u.file.origFilename = XtNewString(filename);
|
|
objectData->u.file.origHostname = XtNewString(hostname);
|
|
objectData->u.file.hostIndex = hostId;
|
|
objectData->u.file.baseFilename = _DtBasename(filename);
|
|
objectData->type = -1;
|
|
|
|
/* Hash the directory name */
|
|
if ( (dirName = _DtDirname(filename)) == NULL )
|
|
{
|
|
if ( request->cwdDir )
|
|
dirName = XtNewString(request->cwdDir);
|
|
else
|
|
{
|
|
/* Default to current directory */
|
|
dirName = _DtFindCwd();
|
|
}
|
|
}
|
|
else if ( dirName[0] != '/' )
|
|
{
|
|
/*
|
|
* We have been provided with a relative path name
|
|
* interpret it relative to the context directory.
|
|
*/
|
|
String tmpName;
|
|
|
|
if ( request->cwdDir )
|
|
tmpName=XtNewString(request->cwdDir);
|
|
else
|
|
tmpName=_DtFindCwd();
|
|
|
|
tmpName=XtRealloc(tmpName,strlen(tmpName)+strlen(dirName) +2);
|
|
(void)strcat(tmpName,"/");
|
|
(void)strcat(tmpName,dirName);
|
|
XtFree(dirName);
|
|
dirName=tmpName;
|
|
}
|
|
|
|
if ( objectData->u.file.baseFilename == NULL || dirName == NULL )
|
|
{
|
|
/* Invalidly formed file name */
|
|
InvalidFilename(w, request->clonedAction->label, filename);
|
|
_DtFreeRequest (request);
|
|
XtFree(dirName);
|
|
XtFree(objectData->u.file.origFilename);
|
|
XtFree(objectData->u.file.origHostname);
|
|
XtFree(objectData->u.file.baseFilename);
|
|
return(True);
|
|
}
|
|
|
|
objectData->u.file.dirIndex = _DtAddEntry(dirName, &request->dirNames,
|
|
&request->numDirNames);
|
|
SET_UNKNOWN_IF_DIR(objectData->mask);
|
|
SET_FILE_OBJ(objectData->mask);
|
|
/*
|
|
* default file objects are treated as writable/returned objects.
|
|
*/
|
|
SET_WRITE_OBJ(objectData->mask);
|
|
|
|
/*
|
|
* If a type has been provided for this file -- use it.
|
|
* otherwise -- look up the type.
|
|
*/
|
|
if (typeFile)
|
|
{
|
|
if ( filetype && *filetype )
|
|
objectData->type = _DtDtsMMStringToBoson(filetype);
|
|
else
|
|
{
|
|
TryToTypeFile(objectData,request->hostNames[hostId],
|
|
dirName, objectData->u.file.baseFilename,
|
|
&resolvedPath);
|
|
|
|
if ( resolvedPath )
|
|
{
|
|
struct stat sbuf;
|
|
if ( !stat(resolvedPath,&sbuf) )
|
|
{
|
|
/* successful stat of file -- check permissions */
|
|
if ( !( sbuf.st_mode&S_IWOTH
|
|
|| sbuf.st_mode&S_IWGRP
|
|
|| sbuf.st_mode&S_IWUSR) )
|
|
{
|
|
RESET_WRITE_OBJ(objectData->mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
XtFree(resolvedPath);
|
|
XtFree(dirName);
|
|
return(False);
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns a string representing the current working directory
|
|
* for this process. This string must be freed up by the caller.
|
|
* NOTE:This function does not replace sym_links with real paths.
|
|
* This may be useful on networks where nfs mounts and symbolic
|
|
* consistently named symbolic links are used to give the
|
|
* impression of a single large network file system.
|
|
*/
|
|
|
|
String
|
|
_DtFindCwd( void )
|
|
|
|
{
|
|
String tmp = 0;
|
|
char buf[MAXPATHLEN + 1];
|
|
|
|
if ((tmp = getcwd(buf, MAXPATHLEN)) == NULL)
|
|
{
|
|
_DtSimpleError(
|
|
DtProgName, DtError, NULL,
|
|
"getcwd(): unable to get current directory", NULL);
|
|
tmp = "/";
|
|
}
|
|
return (XtNewString(tmp));
|
|
}
|
|
|
|
|
|
/*
|
|
* Generic function which checks to see if the specified string is
|
|
* already entered in the passed-in array; if so, then it will return
|
|
* the index of the existing entry within the array; if not, then it
|
|
* will grow the array, add the string into it, and then return the
|
|
* new index.
|
|
*/
|
|
|
|
static int
|
|
_DtAddEntry(
|
|
register String string,
|
|
register String **arrayPtr,
|
|
register int *sizePtr )
|
|
|
|
{
|
|
register int i;
|
|
|
|
/* See if the string is already in the array */
|
|
for (i = 0; i < *sizePtr; i++)
|
|
{
|
|
if (strcmp(string, (*arrayPtr)[i]) == 0)
|
|
return(i);
|
|
}
|
|
|
|
/* Add the string */
|
|
i = *sizePtr;
|
|
(*sizePtr)++;
|
|
(*arrayPtr) = (String *)XtRealloc((String)*arrayPtr,
|
|
(Cardinal)(sizeof(String) * (*sizePtr)));
|
|
(*arrayPtr)[i] = XtNewString(string);
|
|
return(i);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function will type the indicated file, only if it is the first
|
|
* parameter file; this is to improve performance, since in many cases,
|
|
* only the first argument is used to 'type' the action, and the others
|
|
* never need to be 'typed'.
|
|
*/
|
|
|
|
static void
|
|
TryToTypeFile(
|
|
ObjectData *obj,
|
|
char * host,
|
|
char * dir,
|
|
char * file,
|
|
char **resolvedPath )
|
|
|
|
{
|
|
/* Follow the link when typing files */
|
|
obj->type = LinkToTypeQuark(host, dir, file, resolvedPath);
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a file, follow any links, and base the filetype off of the
|
|
* final file, not the link we are passed.
|
|
*/
|
|
|
|
static int
|
|
LinkToTypeQuark(
|
|
char * host,
|
|
char * dir,
|
|
char * file,
|
|
char **resolvedPath )
|
|
|
|
{
|
|
char * path;
|
|
char link_path[MAXPATHLEN + 1];
|
|
char file_name[MAXPATHLEN + 1];
|
|
int link_len;
|
|
char * end;
|
|
int history_count;
|
|
int history_size;
|
|
char ** history;
|
|
int i;
|
|
char * dtype;
|
|
DtShmBoson dquark;
|
|
|
|
/* Used to check for symbolic link loops */
|
|
history_count = 0;
|
|
history_size = 100;
|
|
history = (char **)XtMalloc(sizeof(char *) * history_size);
|
|
|
|
path = _DtActMapFileName(host, dir, file, NULL);
|
|
if (path == NULL)
|
|
{
|
|
*resolvedPath=NULL;
|
|
return(-1);
|
|
}
|
|
strcpy(file_name, path);
|
|
XtFree(path);
|
|
|
|
while ((link_len = readlink(file_name, link_path, MAXPATHLEN)) > 0)
|
|
{
|
|
link_path[link_len] = '\0';
|
|
|
|
/* Force the link to be an absolute path, if necessary */
|
|
if (link_path[0] != '/')
|
|
{
|
|
/* Relative paths are relative to the current directory */
|
|
end = DtStrrchr(file_name, '/') + 1;
|
|
*end = '\0';
|
|
strcat(file_name, link_path);
|
|
}
|
|
else
|
|
strcpy(file_name, link_path);
|
|
|
|
/* Check for a recursive loop; abort if found */
|
|
for (i = 0; i < history_count; i++)
|
|
{
|
|
if (strcmp(file_name, history[i]) == 0)
|
|
{
|
|
/* Drop back to last non-recursive portion of the path */
|
|
strcpy(file_name, history[history_count-1]);
|
|
for (i = 0; i < history_count; i++)
|
|
XtFree(history[i]);
|
|
XtFree((char *)history);
|
|
dtype = DtDtsFileToDataType(file_name);
|
|
dquark = _DtDtsMMStringToBoson(dtype);
|
|
DtDtsFreeDataType(dtype);
|
|
*resolvedPath = XtNewString(file_name);
|
|
return(dquark);
|
|
}
|
|
}
|
|
|
|
/* Add to the history list */
|
|
if (history_count >= history_size)
|
|
{
|
|
history_size += 100;
|
|
history = (char **)XtRealloc((char *)history,
|
|
sizeof(char *) * history_size);
|
|
}
|
|
history[history_count++] = XtNewString(file_name);
|
|
}
|
|
|
|
/* Free up the history list */
|
|
for (i = 0; i < history_count; i++)
|
|
XtFree(history[i]);
|
|
XtFree((char *)history);
|
|
|
|
dtype = DtDtsFileToDataType(file_name);
|
|
dquark = _DtDtsMMStringToBoson(dtype);
|
|
DtDtsFreeDataType(dtype);
|
|
*resolvedPath = XtNewString(file_name);
|
|
return(dquark);
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a request, find the action to which it maps, and see if enough
|
|
* parameters were supplied to allow the action to be started. It's
|
|
* possible we may need to bring up a dialog to collect more data, or
|
|
* we may need to invoke multiple actions.
|
|
*
|
|
* The first time an action request is processed, we will check the
|
|
* parameter situation, and will prompt the user, if necessary. The
|
|
* second time the action request is processed (typically when the
|
|
* user closes the parameter collecting dialog), we will simply invoke
|
|
* the action with whatever we have; the user will not be prompted a
|
|
* second time for any missing parameters.
|
|
*
|
|
* If the request is processed (True is returned), then it is up to the
|
|
* caller to free up the request structure.
|
|
*/
|
|
|
|
static Boolean
|
|
ProcessRequest(
|
|
Widget w,
|
|
register ActionRequest *request )
|
|
|
|
{
|
|
int unused;
|
|
register ActionPtr action;
|
|
int numPrompts;
|
|
PromptEntry * prompts;
|
|
DtShmBoson actionQuark;
|
|
Tt_status status = TT_OK;
|
|
|
|
/* See if this is the first pass for the request */
|
|
if (request->clonedAction == NULL)
|
|
{
|
|
|
|
/* Always start with the first host, when processing a request */
|
|
request->hostIndex = 0;
|
|
|
|
/* Find the action DB entry which we map to */
|
|
actionQuark = _DtDtsMMStringToBoson(request->actionName);
|
|
RESET_TOO_MANY_MAPS(request->mask);
|
|
|
|
if (actionQuark == -1 || (action = _DtActionFindDBEntry(request, actionQuark)) == NULL)
|
|
{
|
|
/*
|
|
* No action label is available here for error dialogs
|
|
*/
|
|
if (IS_TOO_MANY_MAPS(request->mask))
|
|
{
|
|
MapError (w, request->actionName);
|
|
|
|
}
|
|
else if (request->numObjects > 0)
|
|
{
|
|
if (IS_FILE_OBJ(request->objects[0].mask))
|
|
{
|
|
NoActionError(w, actionQuark,
|
|
request->actionName,
|
|
(char *)_DtDtsMMBosonToString(request->objects[0].type),
|
|
request->hostNames[request->objects[0].u.file.hostIndex],
|
|
request->dirNames[request->objects[0].u.file.dirIndex],
|
|
request->objects[0].u.file.baseFilename);
|
|
}
|
|
else if ( IS_BUFFER_OBJ(request->objects[0].mask) )
|
|
{
|
|
/*
|
|
* RWV -- may have to modify this call to generate a
|
|
* message more suitable for buffer objects.
|
|
*/
|
|
NoActionError(w, actionQuark,
|
|
request->actionName,
|
|
(char *)_DtDtsMMBosonToString(request->objects[0].type),
|
|
NULL, /* host */
|
|
NULL, /* dir */
|
|
"Memory Object" /* filename */);
|
|
} else
|
|
myassert(0 /* should never get here */ );
|
|
|
|
/* fdt: add code for strings
|
|
* else if (IS_STRING_OBJ(request->objects[0].mask))
|
|
*/
|
|
}
|
|
else
|
|
NoActionError(w, actionQuark, request->actionName,
|
|
NULL, NULL, NULL, NULL);
|
|
|
|
/*
|
|
* If we are in the middle of reprocessing a single argument
|
|
* action, then continue with the next parameter. Otherwise,
|
|
* this error terminates the request, so return.
|
|
*/
|
|
if (IS_REPROCESSING(request->mask) && MoreArgumentsToProcess(request))
|
|
return(ProcessRequest(w, request));
|
|
|
|
/*
|
|
* We were never able to start this action.
|
|
*/
|
|
{
|
|
_DtActInvRecT *invRecP = _DtActFindInvRec(request->invocId);
|
|
if (invRecP) SET_INV_ERROR(invRecP->state);
|
|
}
|
|
return(True);
|
|
}
|
|
|
|
request->clonedAction = action;
|
|
|
|
/*
|
|
* If this is a ToolTalk message, then before proceeding any further,
|
|
* make sure we can get connected to a ToolTalk session. If we can't,
|
|
* then we need to bail out.
|
|
*/
|
|
if (IS_TT_MSG(action->mask) &&
|
|
(status = _DtInitializeToolTalk(NULL)) != TT_OK)
|
|
{
|
|
NoToolTalkConnectionError(w, request->clonedAction->label, status);
|
|
{
|
|
_DtActInvRecT *invRecP = _DtActFindInvRec(request->invocId);
|
|
if (invRecP) SET_INV_ERROR(invRecP->state);
|
|
}
|
|
return(True);
|
|
}
|
|
|
|
/* Determine how we are sitting with parameters */
|
|
unused = MatchParamsToAction(request, &numPrompts, &prompts);
|
|
request->objsUsed = request->numObjects - unused;
|
|
myassert(request->objsUsed >= 0);
|
|
|
|
/*
|
|
* Do we need to create a prompt dialog?
|
|
* NOTE: if the action requires the user to be prompted, but the
|
|
* user has supplied extra parameters, so he will be asked
|
|
* to abort or continue, do the abort/continue dialog BEFORE
|
|
* the prompt dialog; there's little sense in collecting
|
|
* additional input if the user is going to abort the action!
|
|
*/
|
|
if ((prompts != NULL) &&
|
|
((unused == 0) || IS_ARG_SINGLE_ARG(action->mask) ||
|
|
IS_ARG_NONE_FOUND(action->mask)))
|
|
{
|
|
CreatePromptDialog(w, request, numPrompts, prompts);
|
|
XtFree((char *)prompts);
|
|
return(False);
|
|
}
|
|
|
|
/* Were too many parameters supplied? */
|
|
else if (unused > 0)
|
|
{
|
|
/*
|
|
* If the action only needs a single parameter, then we need
|
|
* to fire off multiple instances of the action; otherwise,
|
|
* prompt the user to continue or abort. An action requiring
|
|
* no parameters is also treated like a single parameter action.
|
|
*/
|
|
if (IS_ARG_SINGLE_ARG(action->mask) || IS_ARG_NONE_FOUND(action->mask))
|
|
{
|
|
|
|
PrepareAndExecuteAction(w, request);
|
|
|
|
/* See if there are still more parameters to be processed */
|
|
if (MoreArgumentsToProcess(request))
|
|
return(ProcessRequest(w, request));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Postpone any further processing until the user either
|
|
* tells us to continue, or abort.
|
|
*/
|
|
CreateContinueDialog(w, request, numPrompts, prompts);
|
|
XtFree((char *)prompts);
|
|
return(False);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PrepareAndExecuteAction(w, request);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
PrepareAndExecuteAction(w, request);
|
|
action = request->clonedAction;
|
|
|
|
/*
|
|
* If this is a single argument action, and we have more parameters
|
|
* waiting to be processed, then continue processing them.
|
|
*/
|
|
if ((IS_ARG_SINGLE_ARG(action->mask) || IS_ARG_NONE_FOUND(action->mask))
|
|
&& (MoreArgumentsToProcess(request)))
|
|
{
|
|
return(ProcessRequest(w, request));
|
|
}
|
|
}
|
|
|
|
return(True);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is called at the point where we have collected all of the
|
|
* information needed to actually initiate the action. We will use the
|
|
* set of arguments passed into _DtActionInvoke(), along with any values
|
|
* supplied through the prompt dialog. It is also at this point that
|
|
* the thread of control will split, dependent upon the type of action
|
|
* being executed (Command Invoker, Tooltalk).
|
|
*/
|
|
static void
|
|
PrepareAndExecuteAction(
|
|
Widget w,
|
|
register ActionRequest *request )
|
|
|
|
{
|
|
char * relPathHost;
|
|
char * relPathDir;
|
|
register int i;
|
|
register ActionPtr action = request->clonedAction;
|
|
int argNum;
|
|
_DtActInvRecT *invp; /* pointer to invocation record */
|
|
_DtActChildRecT *childrecp; /* pointer to child record */
|
|
|
|
/*
|
|
* We have gathered all the information necessary to invoke
|
|
* this action all dialogs have been posted and processed.
|
|
* Now create the action invocation record -- unless we are
|
|
* in the midst of reprocessing an already invoked action.
|
|
*/
|
|
invp = _DtActFindInvRec(request->invocId);
|
|
myassert(invp);
|
|
SET_INV_WORKING(invp->state);
|
|
|
|
/*
|
|
* Allocate a child rec -- fill it in
|
|
*/
|
|
if ( (childrecp = _DtActAllocChildRec(invp)) != NULL )
|
|
{
|
|
request->childId = childrecp->childId;
|
|
|
|
childrecp->childState = _DtActCHILD_PENDING_START;
|
|
childrecp->mask = action->mask;
|
|
|
|
childrecp->numObjects = request->objsUsed;
|
|
}
|
|
else
|
|
myassert( 0 /* Unable to allocate childRec */ );
|
|
|
|
|
|
/*
|
|
* Before proceeding, we need to determine what host and directory
|
|
* will be used when resolving relative pathnames.
|
|
*/
|
|
__ExtractCWD(request, &relPathHost, &relPathDir, False);
|
|
if (IS_CMD(action->mask))
|
|
{
|
|
/*
|
|
* All buffer objects must be placed into temporary files for
|
|
* command actions. This has already been done when the
|
|
* request structure was created.
|
|
*/
|
|
if (childrecp && childrecp->numObjects > 0)
|
|
{
|
|
childrecp->argMap =
|
|
(_DtActArgMap *)XtCalloc(childrecp->numObjects,
|
|
sizeof(_DtActArgMap));
|
|
|
|
for ( i = 0; i < childrecp->numObjects && i < invp->ac; i++ )
|
|
{
|
|
childrecp->argMap[i].argN = i+1; /* ignored for CMD actions */
|
|
childrecp->argMap[i].argIdx =
|
|
i + request->objOffset; /* idx into invp->info[] */
|
|
}
|
|
}
|
|
ProcessCommandInvokerRequest(w, request, relPathHost, relPathDir);
|
|
}
|
|
else if (IS_TT_MSG(action->mask))
|
|
{
|
|
if (childrecp)
|
|
{
|
|
/*
|
|
* create argmap for returnable arguments --
|
|
* i.e. those appearing in TT_ARGn_VALUE fields.
|
|
*
|
|
* The requirement is that one and only one action argument may
|
|
* appear in a TT_ARGn_VALUE field.
|
|
*
|
|
* argMap is a sparse array which maps TT_ARGn_VALUEs to input
|
|
* parameters. If a TT_ARGn_VALUE does not have an input parameter
|
|
* as a value then the sentinel value "-1" is provided as the index.
|
|
* Allocate enough space for all the TT_ARGn_VALUEs plus one for
|
|
* TT_FILE.
|
|
*
|
|
* The elements of the argMap array then represent:
|
|
* argMap[ TT_ARG0, TT_ARG1, ...,TT_ARGN, TT_FILE]
|
|
*/
|
|
|
|
childrecp->argMap =
|
|
(_DtActArgMap *)XtCalloc( action->u.tt_msg.value_count + 1,
|
|
sizeof(_DtActArgMap));
|
|
|
|
for ( i = 0; i < action->u.tt_msg.value_count; i++)
|
|
{
|
|
/*
|
|
* Set index value to "-1". This value will indicate
|
|
* TT_ARGn_VALUES which are NOT associated with input
|
|
* parameters (action arguments). If there is an action
|
|
* argument associated with this TT_ARGn_VALUE we will set
|
|
* it below.
|
|
*/
|
|
childrecp->argMap[i].argIdx = -1;
|
|
childrecp->argMap[i].argN = i;
|
|
|
|
/* null argn value is valid -- so check MsgParts*/
|
|
if (!action->u.tt_msg.tt_argn_value[i].numMsgParts)
|
|
continue;
|
|
|
|
if (action->
|
|
u.tt_msg.tt_argn_value[i].parsedMessage[0].keyword
|
|
!= ARG)
|
|
continue;
|
|
|
|
/*
|
|
* TT_ARGn_VALUE fields should have only one arg keyword.
|
|
*/
|
|
myassert(action->u.tt_msg.tt_argn_value[i].numMsgParts == 1);
|
|
argNum =action->
|
|
u.tt_msg.tt_argn_value[i].parsedMessage[0].argNum;
|
|
|
|
if ( ( argNum > 0 ) && ( argNum <= invp->ac ) )
|
|
{
|
|
/* The ith message part must be returned */
|
|
childrecp->argMap[i].argIdx =
|
|
argNum + request->objOffset - 1;
|
|
myassert( childrecp->argMap[i].argIdx >= 0 );
|
|
}
|
|
}
|
|
/*
|
|
* Add an argMap entry for the value of the TT_FILE field.
|
|
* Tooltalk (e.g. media messages) sometimes uses this field
|
|
* to pass values such as file names to the message receipient.
|
|
* If the TT_FILE field has a single ARG keyword
|
|
* then record that parameter number otherwise record "-1" as
|
|
* was done for the value arguments above.
|
|
*/
|
|
childrecp->argMap[i].argIdx = -1;
|
|
childrecp->argMap[i].argN = -1; /* Use "-1" as TT_FILE entry idx */
|
|
if (action->u.tt_msg.tt_file.numMsgParts
|
|
&& action->u.tt_msg.tt_file.parsedMessage[0].keyword == ARG )
|
|
{
|
|
argNum =action->
|
|
u.tt_msg.tt_file.parsedMessage[0].argNum;
|
|
if ( ( argNum > 0 ) && ( argNum <= invp->ac ) )
|
|
{
|
|
/* The ith message part should be the last argMap entry */
|
|
childrecp->argMap[i].argIdx =
|
|
argNum + request->objOffset - 1;
|
|
myassert( childrecp->argMap[i].argIdx >= 0 );
|
|
}
|
|
}
|
|
|
|
}
|
|
_DtProcessTtRequest(w, request, relPathHost, relPathDir);
|
|
}
|
|
|
|
/*
|
|
* For now we are through invoking this child.
|
|
* There may still be more children to invoke or we may have to
|
|
* re-invoke this child (e.g. multi-host processing for commands).
|
|
*/
|
|
SET_INV_DONE(invp->state);
|
|
|
|
/* Free up the path information */
|
|
XtFree(relPathHost);
|
|
XtFree(relPathDir);
|
|
}
|
|
|
|
|
|
/*
|
|
* Determine the CWD to use; this information can be used to both
|
|
* resolve relative filepaths, and to set the CWD used when executing
|
|
* a command invoker request. When resolving relative paths, the
|
|
* information specified for the first argument is not used (see case 2
|
|
* below). It is determined using the following algorithm:
|
|
*
|
|
* 1) Use the CWD specified in the action (if a cmd invoker action).
|
|
* 2) If told to use the objects, then use the directory where the
|
|
* object lives (if a regular file), or the object itself (if
|
|
* it's a directory.
|
|
* 3) Use the CWD passed into _) by the application.
|
|
* 4) Use the physical CWD of the application.
|
|
*
|
|
* Both the host and directory paths must by freed by the caller.
|
|
*/
|
|
static void
|
|
__ExtractCWD(
|
|
register ActionRequest *request,
|
|
char ** hostPtr,
|
|
char ** dirPtr,
|
|
Boolean useObjectInfo )
|
|
|
|
{
|
|
String msg;
|
|
String lastCh;
|
|
int lastChLen;
|
|
register ActionPtr action = request->clonedAction;
|
|
|
|
/* Only dropped objects will have been 'typed' at this point */
|
|
if (useObjectInfo && (IS_CMD(action->mask)) && (request->numObjects > 0) &&
|
|
(request->objects[0].type >= 0) &&
|
|
IS_FILE_OBJ(request->objects[0].mask))
|
|
{
|
|
if (action->u.cmd.contextHost != NULL)
|
|
*hostPtr = XtNewString(action->u.cmd.contextHost);
|
|
else
|
|
{
|
|
*hostPtr = XtNewString(
|
|
request->hostNames[request->objects[0].u.file.hostIndex]);
|
|
}
|
|
|
|
if (IS_UNKNOWN_IF_DIR(request->objects[0].mask))
|
|
{
|
|
String nfsPath;
|
|
char *theHost, *theDir;
|
|
struct stat statInfo;
|
|
|
|
RESET_UNKNOWN_IF_DIR(request->objects[0].mask);
|
|
|
|
/*
|
|
* The file may not have been checked yet, if it was never
|
|
* referenced in the execution string; so .. we'll check
|
|
* here.
|
|
*/
|
|
theHost = request->hostNames[request->objects[0].u.file.hostIndex];
|
|
theDir = request->dirNames[request->objects[0].u.file.dirIndex];
|
|
nfsPath = _DtActMapFileName(theHost, theDir,
|
|
request->objects[0].u.file.baseFilename, NULL);
|
|
|
|
if (nfsPath && (stat(nfsPath, &statInfo) == 0) &&
|
|
((statInfo.st_mode & S_IFMT) == S_IFDIR))
|
|
{
|
|
SET_DIR_OBJ(request->objects[0].mask);
|
|
}
|
|
XtFree(nfsPath);
|
|
}
|
|
|
|
if (IS_DIR_OBJ(request->objects[0].mask))
|
|
{
|
|
if (action->u.cmd.contextDir != NULL)
|
|
*dirPtr = XtNewString(action->u.cmd.contextDir);
|
|
else
|
|
{
|
|
*dirPtr = XtMalloc((Cardinal)
|
|
(strlen(request->dirNames[request->objects[0].u.file.dirIndex]) +
|
|
strlen(request->objects[0].u.file.baseFilename) + 2));
|
|
strcpy(*dirPtr,
|
|
request->dirNames[request->objects[0].u.file.dirIndex]);
|
|
|
|
DtLastChar(*dirPtr, &lastCh, &lastChLen);
|
|
if ((lastChLen != 1) || (*lastCh != '/'))
|
|
(void)strcat(*dirPtr, "/");
|
|
|
|
(void)strcat(*dirPtr, request->objects[0].u.file.baseFilename);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (action->u.cmd.contextDir != NULL)
|
|
*dirPtr = XtNewString(action->u.cmd.contextDir);
|
|
else
|
|
{
|
|
*dirPtr = XtNewString(
|
|
request->dirNames[request->objects[0].u.file.dirIndex]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Use specified context, or get process context, if necessary */
|
|
if (IS_CMD(action->mask) && (action->u.cmd.contextHost != NULL))
|
|
*hostPtr = XtNewString(action->u.cmd.contextHost);
|
|
else if (request->cwdHost)
|
|
*hostPtr = XtNewString(request->cwdHost);
|
|
else
|
|
{
|
|
*hostPtr = _DtGetLocalHostName();
|
|
}
|
|
|
|
if (IS_CMD(action->mask) && (action->u.cmd.contextDir != NULL))
|
|
*dirPtr = XtNewString(action->u.cmd.contextDir);
|
|
else if (request->cwdDir)
|
|
*dirPtr = XtNewString(request->cwdDir);
|
|
else
|
|
*dirPtr = _DtFindCwd();
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is used to prepare for the continued processing of
|
|
* the parameters, when the action is a single argument action. It
|
|
* free up any data which was associated with the previous parameter,
|
|
* and cascades up any remaining parameters in the object array.
|
|
*/
|
|
|
|
static Boolean
|
|
MoreArgumentsToProcess(
|
|
register ActionRequest *request )
|
|
|
|
{
|
|
register int i;
|
|
char * path;
|
|
char * dtype;
|
|
|
|
if (request->numObjects <= 1)
|
|
{
|
|
return(False);
|
|
}
|
|
else
|
|
{
|
|
/* Repeat processing for the next argument */
|
|
|
|
/* Cascade up the remaining, unprocess parameters */
|
|
if (IS_FILE_OBJ(request->objects[0].mask))
|
|
{
|
|
XtFree(request->objects[0].u.file.origFilename);
|
|
XtFree(request->objects[0].u.file.origHostname);
|
|
XtFree(request->objects[0].u.file.baseFilename);
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: Add support for strings and buffers here
|
|
* else if (IS_BUFFER_OBJ(request->objects[0].mask))
|
|
* XtFree(request->objects[0].u.buffer.buffer);
|
|
* else if (IS_STRING_OBJ(request->objects[0].mask))
|
|
* XtFree(request->objects[0].u.string.string);
|
|
*/
|
|
for (i = 0; i < (request->numObjects - 1); i++)
|
|
{
|
|
request->objects[i] = request->objects[i+1];
|
|
}
|
|
request->numObjects--;
|
|
request->objOffset++;
|
|
request->objsUsed = 0;
|
|
request->childId = 0;
|
|
|
|
/* Free up our previously cloned action */
|
|
_DtFreeActionStruct(request->clonedAction);
|
|
request->clonedAction = NULL;
|
|
|
|
/* Free up any leftover prompt strings */
|
|
for (i = 0; i < request->numPromptInputs; i++)
|
|
XtFree(request->promptInputs[i]);
|
|
XtFree((char *)request->promptInputs);
|
|
request->promptInputs = NULL;
|
|
request->numPromptInputs = 0;
|
|
SET_REPROCESSING(request->mask);
|
|
XtFree(request->badHostList);
|
|
request->badHostList = NULL;
|
|
XtFree(request->currentHost);
|
|
request->currentHost = NULL;
|
|
request->hostIndex = 0;
|
|
|
|
/* Type the object, if possible */
|
|
if (IS_FILE_OBJ(request->objects[0].mask))
|
|
{
|
|
if ((request->objects[0].u.file.hostIndex >= 0) &&
|
|
(request->objects[0].u.file.dirIndex >= 0))
|
|
{
|
|
path = _DtActMapFileName(
|
|
request->hostNames[request->objects[0].u.file.hostIndex],
|
|
request->dirNames[request->objects[0].u.file.dirIndex],
|
|
request->objects[0].u.file.baseFilename, NULL);
|
|
dtype = DtDtsFileToDataType(path);
|
|
request->objects[0].type = _DtDtsMMStringToBoson(dtype);
|
|
DtDtsFreeDataType(dtype);
|
|
XtFree(path);
|
|
}
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: add support for buffers and strings here
|
|
* else if (IS_BUFFER_OBJ(request->objects[0].mask))
|
|
* {
|
|
* }
|
|
* else if (IS_STRING_OBJ(request->objects[0].mask))
|
|
* {
|
|
* }
|
|
*/
|
|
|
|
return(True);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Functions For Cloning And Free Structures */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
|
|
/*
|
|
* At the point that a request is sent, we need to save a copy of the
|
|
* request, for future reference. We need to clone the request, since
|
|
* the original request structure may get modified between the time we
|
|
* send the message, and the time we need to reference it in the future.
|
|
*/
|
|
|
|
ActionRequest *
|
|
_DtCloneRequest (
|
|
ActionRequest * request)
|
|
|
|
{
|
|
ActionRequest * newRequest;
|
|
int i;
|
|
|
|
newRequest = (ActionRequest *)XtMalloc((Cardinal)sizeof(ActionRequest));
|
|
|
|
/*
|
|
* Structure assignment to clone all scalar values
|
|
* If a value is not explicitly set then it defaults to the same value
|
|
* as in the original request. Pointers to malloc-ed memory should all
|
|
* be replaced with pointers to a copy of the region.
|
|
*/
|
|
(*newRequest) = (*request);
|
|
|
|
newRequest->actionName = XtNewString(request->actionName);
|
|
|
|
if (request->numObjects > 0) {
|
|
newRequest->objects = (ObjectData *)XtMalloc(sizeof(ObjectData) *
|
|
request->numObjects);
|
|
|
|
for (i = 0; i < request->numObjects; i++)
|
|
{
|
|
newRequest->objects[i] = request->objects[i];
|
|
if (IS_FILE_OBJ(request->objects[i].mask))
|
|
{
|
|
newRequest->objects[i].u.file.origFilename =
|
|
XtNewString(request->objects[i].u.file.origFilename);
|
|
newRequest->objects[i].u.file.origHostname =
|
|
XtNewString(request->objects[i].u.file.origHostname);
|
|
newRequest->objects[i].u.file.baseFilename =
|
|
XtNewString(request->objects[i].u.file.baseFilename);
|
|
}
|
|
else if ( IS_BUFFER_OBJ(request->objects[i].mask) )
|
|
{
|
|
/*
|
|
* RWV:
|
|
* Since we are creating tmp files for all buffers
|
|
* we should never have to copy a buffer's contents.
|
|
*
|
|
* We should never reach this code because the FILE_OBJ
|
|
* bit is set when we create tmp files for buffers.
|
|
*/
|
|
myassert(0);
|
|
/*
|
|
* RWV:
|
|
* Can we get by without copying buffer object contents?
|
|
* if so -- how do we avoid freeing it twice OR
|
|
* not freeing it at all?
|
|
*/
|
|
/* make a copy of the buffer */
|
|
if ( request->objects[i].u.buffer.bp )
|
|
{
|
|
myassert(newRequest->objects[i].u.buffer.size == request->objects[i].u.buffer.size);
|
|
newRequest->objects[i].u.buffer.bp =
|
|
XtMalloc( request->objects[i].u.buffer.size );
|
|
memcpy(newRequest->objects[i].u.buffer.bp,
|
|
request->objects[i].u.buffer.bp,
|
|
newRequest->objects[i].u.buffer.size);
|
|
}
|
|
}
|
|
else
|
|
myassert(0 /* no other object types supported */ );
|
|
}
|
|
}
|
|
|
|
newRequest->numPromptInputs = request->numPromptInputs;
|
|
if (request->numPromptInputs > 0) {
|
|
newRequest->promptInputs = (char **)XtMalloc(sizeof(char *) *
|
|
request->numPromptInputs);
|
|
for (i = 0; i < request->numPromptInputs; i++)
|
|
newRequest->promptInputs[i] = XtNewString(request->promptInputs[i]);
|
|
}
|
|
|
|
newRequest->numHostNames = request->numHostNames;
|
|
if (request->numHostNames > 0) {
|
|
newRequest->hostNames = (char **)XtMalloc(sizeof(char *) *
|
|
request->numHostNames);
|
|
for (i = 0; i < request->numHostNames; i++)
|
|
newRequest->hostNames[i] = XtNewString(request->hostNames[i]);
|
|
}
|
|
|
|
newRequest->numDirNames = request->numDirNames;
|
|
if (request->numDirNames > 0) {
|
|
newRequest->dirNames = (char **)XtMalloc(sizeof(char *) *
|
|
request->numDirNames);
|
|
for (i = 0; i < request->numDirNames; i++)
|
|
newRequest->dirNames[i] = XtNewString(request->dirNames[i]);
|
|
}
|
|
|
|
newRequest->termOpts = XtNewString(request->termOpts);
|
|
newRequest->cwdHost = XtNewString(request->cwdHost);
|
|
newRequest->cwdDir = XtNewString(request->cwdDir);
|
|
|
|
if (request->clonedAction)
|
|
newRequest->clonedAction = CloneActionDBEntry(request->clonedAction);
|
|
else
|
|
newRequest->clonedAction = NULL;
|
|
|
|
newRequest->badHostList = XtNewString(request->badHostList);
|
|
newRequest->currentHost = XtNewString(request->currentHost);
|
|
newRequest->execHost = XtNewString(request->execHost);
|
|
|
|
|
|
return(newRequest);
|
|
}
|
|
|
|
|
|
/*
|
|
* Free up the contents of a request structure
|
|
*/
|
|
|
|
void
|
|
_DtFreeRequest(
|
|
register ActionRequest *request )
|
|
|
|
{
|
|
register int i;
|
|
|
|
XtFree(request->actionName);
|
|
|
|
for (i = 0; i < request->numObjects; i++)
|
|
{
|
|
if (IS_FILE_OBJ(request->objects[i].mask))
|
|
{
|
|
XtFree(request->objects[i].u.file.origFilename);
|
|
XtFree(request->objects[i].u.file.origHostname);
|
|
XtFree(request->objects[i].u.file.baseFilename);
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: Add support for buffers and strings here
|
|
* else if (IS_BUFFER_OBJ(request->objects[i].mask)
|
|
* XtFree(request->objects[i].u.buffer.buffer);
|
|
* else if (IS_STRING_OBJ(request->objects[i].mask)
|
|
* XtFree(request->objects[i].u.string.string);
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Since the objectDataArray was malloced at once
|
|
* we can free it at once.
|
|
*/
|
|
if (request->objects) XtFree((char *)request->objects);
|
|
|
|
for (i = 0; i < request->numPromptInputs; i++)
|
|
XtFree(request->promptInputs[i]);
|
|
if (request->promptInputs) XtFree((char *)request->promptInputs);
|
|
|
|
for (i = 0; i < request->numHostNames; i++)
|
|
XtFree(request->hostNames[i]);
|
|
if (request->hostNames) XtFree((char *)request->hostNames);
|
|
|
|
for (i = 0; i < request->numDirNames; i++)
|
|
XtFree(request->dirNames[i]);
|
|
if (request->dirNames) XtFree((char *)request->dirNames);
|
|
|
|
XtFree(request->termOpts);
|
|
XtFree(request->cwdHost);
|
|
XtFree(request->cwdDir);
|
|
_DtFreeActionStruct(request->clonedAction);
|
|
XtFree(request->badHostList);
|
|
XtFree(request->currentHost);
|
|
XtFree(request->execHost);
|
|
|
|
XtFree ((char *)request);
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a clone of an action DB entry
|
|
*/
|
|
|
|
static ActionPtr
|
|
CloneActionDBEntry(
|
|
register ActionPtr action )
|
|
|
|
{
|
|
register ActionPtr newAction = (ActionPtr)XtMalloc((Cardinal)sizeof(Action));
|
|
register int i;
|
|
|
|
/* Clone each field */
|
|
newAction->action = action->action;
|
|
newAction->file_name_id = action->file_name_id;
|
|
newAction->label = XtNewString(action->label);
|
|
newAction->description = XtNewString(action->description);
|
|
|
|
newAction->type_count = action->type_count;
|
|
if (action->type_count > 0) {
|
|
newAction->arg_types = (DtShmBoson *)XtMalloc(sizeof(DtShmBoson) *
|
|
newAction->type_count);
|
|
for (i = 0; i < newAction->type_count; i++)
|
|
newAction->arg_types[i] = action->arg_types[i];
|
|
}
|
|
else {
|
|
newAction->arg_types = NULL;
|
|
}
|
|
|
|
newAction->arg_count = action->arg_count;
|
|
newAction->mask = action->mask;
|
|
|
|
if (IS_CMD(action->mask))
|
|
{
|
|
cmdAttr * newCmd = &(newAction->u.cmd);
|
|
cmdAttr * oldCmd = &(action->u.cmd);
|
|
|
|
CloneParsedMessage(&(oldCmd->execString), &(newCmd->execString));
|
|
CloneParsedMessage(&(oldCmd->termOpts), &(newCmd->termOpts));
|
|
newCmd->contextDir = XtNewString(oldCmd->contextDir);
|
|
newCmd->contextHost = XtNewString(oldCmd->contextHost);
|
|
CloneParsedMessage(&(oldCmd->execHosts), &(newCmd->execHosts));
|
|
newCmd->execHostCount = oldCmd->execHostCount;
|
|
if (oldCmd->execHostCount > 0) {
|
|
newCmd->execHostArray = (char **)XtMalloc(sizeof(char *) *
|
|
newCmd->execHostCount);
|
|
for (i = 0; i < newCmd->execHostCount; i++)
|
|
newCmd->execHostArray[i] = XtNewString(oldCmd->execHostArray[i]);
|
|
}
|
|
else {
|
|
newCmd->execHostArray = NULL;
|
|
}
|
|
}
|
|
else if (IS_MAP(action->mask))
|
|
{
|
|
newAction->u.map.map_action = action->u.map.map_action;
|
|
}
|
|
else if (IS_TT_MSG(action->mask))
|
|
{
|
|
tt_msgAttr * newMsg = &(newAction->u.tt_msg);
|
|
tt_msgAttr * oldMsg = &(action->u.tt_msg);
|
|
|
|
newMsg->tt_class = oldMsg->tt_class;
|
|
newMsg->tt_scope = oldMsg->tt_scope;
|
|
CloneParsedMessage(&(oldMsg->tt_op), &(newMsg->tt_op));
|
|
CloneParsedMessage(&(oldMsg->tt_file), &(newMsg->tt_file));
|
|
|
|
newMsg->mode_count = oldMsg->mode_count;
|
|
if (oldMsg->mode_count > 0) {
|
|
newMsg->tt_argn_mode =
|
|
(int *)XtMalloc(sizeof(int) * newMsg->mode_count);
|
|
for (i = 0; i < newMsg->mode_count; i++)
|
|
newMsg->tt_argn_mode[i] = oldMsg->tt_argn_mode[i];
|
|
}
|
|
else {
|
|
newMsg->tt_argn_mode = NULL;
|
|
}
|
|
|
|
newMsg->vtype_count = oldMsg->vtype_count;
|
|
newMsg->tt_argn_vtype = CloneParsedMessageArray(oldMsg->tt_argn_vtype,
|
|
oldMsg->vtype_count);
|
|
|
|
newMsg->value_count = oldMsg->value_count;
|
|
newMsg->tt_argn_value = CloneParsedMessageArray(oldMsg->tt_argn_value,
|
|
oldMsg->value_count);
|
|
|
|
newMsg->rep_type_count = oldMsg->rep_type_count;
|
|
if (oldMsg->rep_type_count > 0) {
|
|
newMsg->tt_argn_rep_type = (int *)XtMalloc(sizeof(int) *
|
|
newMsg->rep_type_count);
|
|
for (i = 0; i < newMsg->rep_type_count; i++)
|
|
newMsg->tt_argn_rep_type[i] = oldMsg->tt_argn_rep_type[i];
|
|
}
|
|
else {
|
|
newMsg->tt_argn_rep_type = NULL;
|
|
}
|
|
}
|
|
|
|
return(newAction);
|
|
}
|
|
|
|
|
|
/*
|
|
* Free up the contents of a request structure
|
|
*/
|
|
|
|
void
|
|
_DtFreeActionStruct(
|
|
register ActionPtr action )
|
|
|
|
{
|
|
register int i;
|
|
|
|
if (action == NULL)
|
|
return;
|
|
|
|
XtFree(action->label);
|
|
XtFree(action->description);
|
|
if (action->arg_types) XtFree((char *)action->arg_types);
|
|
|
|
if (IS_CMD(action->mask))
|
|
{
|
|
FreeParsedMessage(&(action->u.cmd.execString));
|
|
FreeParsedMessage(&(action->u.cmd.termOpts));
|
|
XtFree(action->u.cmd.contextDir);
|
|
XtFree(action->u.cmd.contextHost);
|
|
FreeParsedMessage(&(action->u.cmd.execHosts));
|
|
for (i = 0; i < action->u.cmd.execHostCount; i++)
|
|
XtFree(action->u.cmd.execHostArray[i]);
|
|
if (action->u.cmd.execHostArray) {
|
|
XtFree((char *)action->u.cmd.execHostArray);
|
|
}
|
|
}
|
|
else if (IS_TT_MSG(action->mask))
|
|
{
|
|
FreeParsedMessage(&(action->u.tt_msg.tt_op));
|
|
FreeParsedMessage(&(action->u.tt_msg.tt_file));
|
|
if (action->u.tt_msg.tt_argn_mode) {
|
|
XtFree((char *)action->u.tt_msg.tt_argn_mode);
|
|
}
|
|
FreeParsedMessageArray(action->u.tt_msg.tt_argn_vtype,
|
|
action->u.tt_msg.vtype_count);
|
|
FreeParsedMessageArray(action->u.tt_msg.tt_argn_value,
|
|
action->u.tt_msg.value_count);
|
|
if (action->u.tt_msg.tt_argn_rep_type) {
|
|
XtFree((char *)action->u.tt_msg.tt_argn_rep_type);
|
|
}
|
|
}
|
|
|
|
XtFree((char *)action);
|
|
}
|
|
|
|
|
|
static void
|
|
CloneParsedMessage(
|
|
register parsedMsg * old_pmsg,
|
|
register parsedMsg * new_pmsg )
|
|
|
|
{
|
|
int i;
|
|
register MsgComponent * piece;
|
|
register MsgComponent * newPiece;
|
|
|
|
new_pmsg->numMsgParts = old_pmsg->numMsgParts;
|
|
if (old_pmsg->compiledMessage)
|
|
{
|
|
/*
|
|
* Some day these may not always be null-terminated strings
|
|
*/
|
|
new_pmsg->compiledMessage = (char *)XtMalloc(old_pmsg->msgLen);
|
|
memcpy(new_pmsg->compiledMessage,
|
|
old_pmsg->compiledMessage,
|
|
old_pmsg->msgLen);
|
|
new_pmsg->msgLen = old_pmsg->msgLen;
|
|
}
|
|
else
|
|
{
|
|
new_pmsg->compiledMessage = NULL;
|
|
new_pmsg->msgLen = 0;
|
|
}
|
|
|
|
|
|
/* Clone the message components */
|
|
if (old_pmsg->numMsgParts > 0)
|
|
{
|
|
new_pmsg->parsedMessage = (MsgComponent *)
|
|
XtMalloc((Cardinal)(sizeof(MsgComponent) * old_pmsg->numMsgParts));
|
|
|
|
for (i = 0; i < old_pmsg->numMsgParts; i++)
|
|
{
|
|
piece = &(old_pmsg->parsedMessage[i]);
|
|
newPiece = &(new_pmsg->parsedMessage[i]);
|
|
|
|
/* Clone each subcomponent of this message */
|
|
if (piece->precedingText)
|
|
newPiece->precedingText = XtNewString(piece->precedingText);
|
|
else
|
|
newPiece->precedingText = NULL;
|
|
|
|
if (piece->prompt)
|
|
newPiece->prompt = XtNewString(piece->prompt);
|
|
else
|
|
newPiece->prompt = NULL;
|
|
|
|
newPiece->keyword = piece->keyword;
|
|
newPiece->argNum = piece->argNum;
|
|
newPiece->mask = piece->mask;
|
|
}
|
|
}
|
|
else
|
|
new_pmsg->parsedMessage = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Free up the contents of a parsedMsg structure, but not the structure
|
|
* itself (since many of our structures contain in-line instances of
|
|
* the parsedMsg structure).
|
|
*/
|
|
static void
|
|
FreeParsedMessage(
|
|
register parsedMsg * parsedMessage )
|
|
|
|
{
|
|
int i;
|
|
|
|
/* Free up the message components */
|
|
if (parsedMessage->numMsgParts > 0)
|
|
{
|
|
for (i = 0; i < parsedMessage->numMsgParts; i++)
|
|
{
|
|
XtFree(parsedMessage->parsedMessage[i].precedingText);
|
|
XtFree(parsedMessage->parsedMessage[i].prompt);
|
|
}
|
|
XtFree((char *)parsedMessage->parsedMessage);
|
|
}
|
|
|
|
XtFree(parsedMessage->compiledMessage);
|
|
}
|
|
|
|
|
|
/*
|
|
* Allocate an array to hold a copy of all of the parsedMsg structures.
|
|
* This array must be freed eventually by the caller.
|
|
*/
|
|
static parsedMsg *
|
|
CloneParsedMessageArray(
|
|
register parsedMsg * pmsgArray,
|
|
register int count )
|
|
|
|
{
|
|
parsedMsg * newArray;
|
|
int i;
|
|
|
|
if (count == 0)
|
|
return(NULL);
|
|
|
|
newArray = (parsedMsg *)XtMalloc(sizeof(parsedMsg) * count);
|
|
|
|
for (i = 0; i < count; i++)
|
|
CloneParsedMessage(pmsgArray + i, newArray + i);
|
|
|
|
return(newArray);
|
|
}
|
|
|
|
|
|
/*
|
|
* Free up the counted array of parsedMsg structures.
|
|
* The array pointing to them also needs to be freed.
|
|
*/
|
|
static void
|
|
FreeParsedMessageArray(
|
|
register parsedMsg * parsedMessageArray,
|
|
int count )
|
|
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
FreeParsedMessage(parsedMessageArray + i);
|
|
|
|
XtFree((char *)parsedMessageArray);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Functions For Placing Arguments Into A Message String */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
|
|
/*
|
|
* This function takes a 'parsedMsg' structure, and compiles all of its
|
|
* pieces into a single string, replacing keywords as they are encountered.
|
|
* Since a given action request can be made up of multiple pieces, this
|
|
* function uses some static variables to maintain state information between
|
|
* calls for the same action request; passing in 'True' for the 'initialize'
|
|
* parameter for the first call for a given action request will clear out
|
|
* any old static values.
|
|
*/
|
|
|
|
Boolean
|
|
_DtCompileMessagePiece(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir,
|
|
parsedMsg * piece,
|
|
Boolean initialize,
|
|
unsigned long processingMask,
|
|
Boolean ** paramUsed,
|
|
int * promptDataIndex )
|
|
|
|
{
|
|
register int i, j;
|
|
Boolean firstParmUsed;
|
|
register MsgComponent * segment;
|
|
char * compiledMsg = NULL;
|
|
int compiledMsgSize = 0;
|
|
ObjectData tmpObjData;
|
|
static char *sessionHostName= NULL;
|
|
static char *displayHostName = NULL;
|
|
static char *localHostName = NULL;
|
|
|
|
XtFree(piece->compiledMessage);
|
|
piece->compiledMessage = NULL;
|
|
piece->msgLen = 0;
|
|
|
|
if (initialize)
|
|
{
|
|
/*
|
|
* Keep track of which parameters have been used, so that when
|
|
* a %Args% keyword is encountered, we know which parameters
|
|
* should be substituted.
|
|
*/
|
|
*promptDataIndex = 0;
|
|
|
|
if (request->numObjects > 0) {
|
|
*paramUsed = (Boolean *)XtMalloc((Cardinal)(sizeof(Boolean) *
|
|
request->numObjects));
|
|
for (i = 0; i < request->numObjects; i++)
|
|
(*paramUsed)[i] = False;
|
|
}
|
|
}
|
|
|
|
_DtSvcProcessLock();
|
|
/* We need to query our hostname the first time only */
|
|
if ( ! localHostName )
|
|
localHostName = _DtGetLocalHostName();
|
|
|
|
/*
|
|
* Determine the display host name -- default to localHostName for
|
|
* degenerate display names (i.e. :0, unix:0, local:0, ...)
|
|
*/
|
|
if ( ! displayHostName )
|
|
displayHostName = _DtGetDisplayHostName(XtDisplay(w));
|
|
|
|
if ( ! sessionHostName )
|
|
sessionHostName = _DtGetSessionHostName();
|
|
_DtSvcProcessUnlock();
|
|
|
|
/*
|
|
* The message is constructed by taking each of the
|
|
* action segments, replacing any keywords, and then adding the
|
|
* information to the end of the buffer.
|
|
*/
|
|
for (i = 0; i < piece->numMsgParts; i++)
|
|
{
|
|
segment = piece->parsedMessage + i;
|
|
|
|
/* Add any text preceding the keyword */
|
|
if (segment->precedingText)
|
|
{
|
|
compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
|
|
(int)strlen(segment->precedingText));
|
|
(void)strcat(compiledMsg, segment->precedingText);
|
|
}
|
|
|
|
/* Process the keyword */
|
|
switch (segment->keyword)
|
|
{
|
|
case LOCAL_HOST:
|
|
{
|
|
/* Add in the local host name */
|
|
compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
|
|
(int)strlen(localHostName));
|
|
(void)strcat(compiledMsg, localHostName);
|
|
break;
|
|
}
|
|
|
|
case DATABASE_HOST:
|
|
{
|
|
/*
|
|
* Add in the host associated with the DB file from which this
|
|
* action was loaded.
|
|
*/
|
|
char * fullPath;
|
|
char * host;
|
|
|
|
fullPath = _DtDbPathIdToString(request->clonedAction->file_name_id);
|
|
host = _DtHostString(fullPath);
|
|
if (host)
|
|
{
|
|
compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
|
|
host ? (int)strlen(host) : 0);
|
|
(void)strcat(compiledMsg, host);
|
|
XtFree(host);
|
|
}
|
|
XtFree(fullPath);
|
|
break;
|
|
}
|
|
|
|
case DISPLAY_HOST:
|
|
{
|
|
/*
|
|
* Use the displayHostName determined the first time thru
|
|
*/
|
|
compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
|
|
(int)strlen(displayHostName));
|
|
(void)strcat(compiledMsg, displayHostName);
|
|
break;
|
|
}
|
|
|
|
case SESSION_HOST:
|
|
{
|
|
/*
|
|
* Add in the session server host where providing the
|
|
* display management. (i.e. the host where the login client
|
|
* is running.)
|
|
*/
|
|
compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
|
|
(int)strlen(sessionHostName));
|
|
(void)strcat(compiledMsg, sessionHostName);
|
|
break;
|
|
|
|
}
|
|
|
|
case NO_KEYWORD:
|
|
{
|
|
/*
|
|
* If this is an entry which simply collected some user input,
|
|
* then add the user's input to the message buffer.
|
|
* This corresponds to the keywords:
|
|
*
|
|
* %"prompt"%
|
|
* %(String)"prompt"%
|
|
*/
|
|
if (segment->prompt)
|
|
{
|
|
/* Create dummy object; makes processing easier */
|
|
if (ParseFileArgument(w, request, &tmpObjData,
|
|
NULL, request->promptInputs[*promptDataIndex],
|
|
NULL, False))
|
|
{
|
|
XtFree(compiledMsg);
|
|
return(False);
|
|
}
|
|
|
|
if (!InsertArgumentString(w, &compiledMsg, &compiledMsgSize,
|
|
request, &tmpObjData, segment->mask, relPathHost,
|
|
relPathDir, False, 0))
|
|
{
|
|
XtFree(compiledMsg);
|
|
return(False);
|
|
}
|
|
|
|
/* Signal that this prompt has been used */
|
|
(*promptDataIndex)++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ARG:
|
|
{
|
|
if (segment->argNum == ALL_ARGS)
|
|
{
|
|
/* Insert all currently unused parameters */
|
|
for (j = 0, firstParmUsed = False; j < request->numObjects; j++)
|
|
{
|
|
/* Used or empty objects are skipped */
|
|
if ((*paramUsed)[j] == False)
|
|
{
|
|
if (IS_FILE_OBJ(request->objects[j].mask) &&
|
|
request->objects[j].u.file.origFilename)
|
|
{
|
|
if (!InsertArgumentString(w, &compiledMsg,
|
|
&compiledMsgSize,
|
|
request, request->objects+j,
|
|
segment->mask, relPathHost, relPathDir,
|
|
firstParmUsed, processingMask))
|
|
{
|
|
XtFree(compiledMsg);
|
|
return(False);
|
|
}
|
|
firstParmUsed = True;
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: add support for buffers and strings
|
|
* else if (IS_BUFFER_OBJ(request->objects[i].mask) &&
|
|
* request->objects[i].u.buffer.buffer)
|
|
* else if (IS_STRING_OBJ(request->objects[i].mask) &&
|
|
* request->objects[i].u.string.string)
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
else if (segment->argNum <= request->numObjects)
|
|
{
|
|
if (IS_FILE_OBJ(request->objects[segment->argNum-1].mask) &&
|
|
request->objects[segment->argNum-1].u.file.origFilename)
|
|
{
|
|
/* Replace only with the specified argument */
|
|
(*paramUsed)[segment->argNum-1] = True;
|
|
/*
|
|
* All buffer objects have been written to tmp files.
|
|
* This code replaces a reference to an object with its
|
|
* (tmp) file name.
|
|
* Tooltalk processing code elsewhere
|
|
* (ActionTt.c) detects the conditions under which a buffer
|
|
* object reference should be replaced by the buffer contents
|
|
* instead of the tmp file name. (i.e. a value field with a
|
|
* single argument reference with no additional text). In such
|
|
* cases the compiled message string will be ignored.
|
|
*/
|
|
if (!InsertArgumentString(w, &compiledMsg, &compiledMsgSize,
|
|
request, request->objects + segment->argNum - 1,
|
|
segment->mask, relPathHost, relPathDir, False,
|
|
processingMask))
|
|
{
|
|
XtFree(compiledMsg);
|
|
return(False);
|
|
}
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: add support for buffers and strings
|
|
* else if (IS_BUFFER_OBJ(request->objects[i].mask) &&
|
|
* request->objects[i].u.buffer.buffer)
|
|
* else if (IS_STRING_OBJ(request->objects[i].mask) &&
|
|
* request->objects[i].u.string.string)
|
|
*/
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((piece->compiledMessage = compiledMsg) == NULL)
|
|
piece->msgLen = 0;
|
|
else
|
|
piece->msgLen = compiledMsg ? strlen(compiledMsg) + 1: 0;
|
|
return(True);
|
|
}
|
|
|
|
/*
|
|
* Given an object, add it to the end of the message buffer. The
|
|
* object may refer to a file, thus possibly requiring that it be
|
|
* converted to another format.
|
|
*/
|
|
|
|
static Boolean
|
|
InsertArgumentString(
|
|
Widget w,
|
|
register char **bufPtr,
|
|
int * bufSizePtr,
|
|
ActionRequest *request,
|
|
register ObjectData *object,
|
|
unsigned long mask,
|
|
char * relPathHost,
|
|
char * relPathDir,
|
|
Boolean addLeadingSpace,
|
|
unsigned long processingMask )
|
|
|
|
{
|
|
int len;
|
|
String lastCh;
|
|
int lastChLen;
|
|
char * path;
|
|
char * value;
|
|
char * dataType;
|
|
char * mediaAttr;
|
|
|
|
if (processingMask & _DTAct_TT_VTYPE)
|
|
SET_TREAT_AS_FILE(mask);
|
|
|
|
if (IS_TREAT_AS_FILE(mask))
|
|
{
|
|
if (object->type == -1)
|
|
{
|
|
/* Object still needs to be typed */
|
|
if (IS_FILE_OBJ(object->mask))
|
|
{
|
|
char * origInfo = object->u.file.origFilename;
|
|
|
|
ParseFileArgument(w, request, object, NULL, origInfo, NULL, True);
|
|
XtFree(origInfo);
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: add support for buffers and strings
|
|
* else if (IS_BUFFER_OBJ(object->mask))
|
|
* else if (IS_STRING_OBJ(object->mask))
|
|
*/
|
|
}
|
|
|
|
if (IS_FILE_OBJ(object->mask))
|
|
{
|
|
if (processingMask & _DTAct_TT_VTYPE)
|
|
{
|
|
/*
|
|
* Instead of inserting the object referred to by "Arg_n",
|
|
* we need to instead insert the MEDIA attribute for the
|
|
* object. If the MEDIA attribute is not defined for the
|
|
* datatype associated with this object, then use the
|
|
* datatype name itself. If the thing can't be defined, then
|
|
* do nothing.
|
|
*/
|
|
if (object->type != (-1))
|
|
{
|
|
dataType = (char *)_DtDtsMMBosonToString(object->type);
|
|
|
|
if ((path = _DtActMapFileName(
|
|
request->hostNames[object->u.file.hostIndex],
|
|
request->dirNames[object->u.file.dirIndex],
|
|
object->u.file.baseFilename,
|
|
NULL)) == NULL)
|
|
{
|
|
path = NULL;
|
|
}
|
|
|
|
mediaAttr = DtDtsDataTypeToAttributeValue(dataType, "MEDIA",
|
|
path);
|
|
XtFree(path);
|
|
|
|
if (mediaAttr)
|
|
{
|
|
value = XtNewString(mediaAttr);
|
|
DtDtsFreeAttributeValue(mediaAttr);
|
|
}
|
|
else
|
|
value = XtNewString(dataType);
|
|
|
|
*bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(value) + 1);
|
|
if (addLeadingSpace)
|
|
strcat(*bufPtr, " ");
|
|
strcat(*bufPtr, value);
|
|
XtFree(value);
|
|
}
|
|
return(True);
|
|
}
|
|
|
|
if (IS_CMD(request->clonedAction->mask))
|
|
{
|
|
/* Map into a real path, relative to the execution host */
|
|
if ((path = _DtActMapFileName(
|
|
request->hostNames[object->u.file.hostIndex],
|
|
request->dirNames[object->u.file.dirIndex],
|
|
object->u.file.baseFilename,
|
|
request->currentHost)) == NULL)
|
|
{
|
|
AddFailedHostToList(request, request->currentHost);
|
|
return(False);
|
|
}
|
|
|
|
*bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(path) + 1);
|
|
if (addLeadingSpace)
|
|
(void)strcat(*bufPtr, " ");
|
|
strcat(*bufPtr, path);
|
|
XtFree(path);
|
|
}
|
|
else if (IS_TT_MSG(request->clonedAction->mask))
|
|
{
|
|
/*
|
|
* ToolTalk automatically translates the 'filename' field within
|
|
* a message, and expects the incoming name to be relative to
|
|
* the local host. So ... we simply need to map the name to
|
|
* be relative to the local host. However, if this is not the
|
|
* filename, but is instead one of the 'args', then we must
|
|
* insert it in a 'neutral' form.
|
|
*/
|
|
if (processingMask & _DTAct_TT_ARG)
|
|
{
|
|
/* Map into "host:/path" */
|
|
/* fdt: May need to instead map into 'network indep' form */
|
|
InsertUnmappedArgumentString(bufPtr, bufSizePtr, object,
|
|
addLeadingSpace);
|
|
}
|
|
else
|
|
{
|
|
if ((path = _DtActMapFileName(
|
|
request->hostNames[object->u.file.hostIndex],
|
|
request->dirNames[object->u.file.dirIndex],
|
|
object->u.file.baseFilename, NULL)) == NULL)
|
|
{
|
|
return(False);
|
|
}
|
|
|
|
*bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(path) + 1);
|
|
if (addLeadingSpace)
|
|
(void)strcat(*bufPtr, " ");
|
|
strcat(*bufPtr, path);
|
|
XtFree(path);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: add support for buffers and strings
|
|
* else if (IS_BUFFER_OBJ(object->mask))
|
|
* else if (IS_STRING_OBJ(object->mask))
|
|
*/
|
|
}
|
|
else
|
|
InsertUnmappedArgumentString(bufPtr, bufSizePtr, object, addLeadingSpace);
|
|
return(True);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function knows how to insert a string in "host:/path" format;
|
|
* this is essentually an 'unmapped' filename. File arguments which
|
|
* have been preceded by the "(String)" qualifier will be saved in
|
|
* this fashion. Likewise, any filenames (either in "String" or "File"
|
|
* form) for an message will be saved in this format, due to the
|
|
* fact that we don't know the execution host, and thus cannot properly
|
|
* map the filename using the ToolTalk filename mapping functions.
|
|
*/
|
|
|
|
static void
|
|
InsertUnmappedArgumentString(
|
|
register char **bufPtr,
|
|
int * bufSizePtr,
|
|
register ObjectData *object,
|
|
Boolean addLeadingSpace )
|
|
|
|
{
|
|
char * host = NULL;
|
|
int size;
|
|
|
|
/* No mapping is necessary here. */
|
|
if (IS_FILE_OBJ(object->mask))
|
|
{
|
|
size = strlen(object->u.file.origFilename) + 4;
|
|
*bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, size);
|
|
if (addLeadingSpace)
|
|
(void)strcat(*bufPtr, " ");
|
|
strcat(*bufPtr, object->u.file.origFilename);
|
|
}
|
|
/*
|
|
* RWV:
|
|
* Since we use tmp files for buffers and do not support
|
|
* strings; we need not add special code for buffer and
|
|
* string support.
|
|
*/
|
|
/* fdt: add support for buffers and strings
|
|
* else if (IS_BUFFER_OBJ(object->mask))
|
|
* else if (IS_STRING_OBJ(object->mask))
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
* This function checks to see if the message buffer is large enough
|
|
* to hold the current contents + 'count' more bytes. If it is not
|
|
* large enough, then the buffer will be grown. The buffer MUST BE
|
|
* NULL terminated.
|
|
*/
|
|
|
|
static String
|
|
GrowMsgBuffer(
|
|
String buffer,
|
|
int *size,
|
|
int count )
|
|
|
|
{
|
|
int currentBufUsed = buffer ? strlen(buffer) : 0;
|
|
|
|
if ((currentBufUsed + count + 1) >= *size)
|
|
{
|
|
(*size) += (count+1 > 1024) ? count + 1 : 1024;
|
|
buffer = (char *)XtRealloc(buffer, (Cardinal)*size);
|
|
|
|
/* If this is the first alloc for the buffer, then terminate the buffer */
|
|
if(currentBufUsed == 0)
|
|
buffer[0] = '\0';
|
|
}
|
|
|
|
return(buffer);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Functions For Matching Arguments To A Message String */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
|
|
/*
|
|
* If the specified prompt has not already been added to the array of
|
|
* prompt strings, then add it. The exception is for stand-alone
|
|
* prompt strings, which always are added.
|
|
*/
|
|
|
|
static void
|
|
AddPrompt(
|
|
register int argNum,
|
|
String prompt,
|
|
register int *numPrompts,
|
|
register PromptEntry **prompts )
|
|
|
|
{
|
|
register int i;
|
|
|
|
/*
|
|
* Standard arguments only want their prompts entered once.
|
|
* Stand-alone prompts all have argNum == NO_ARG, and each one
|
|
* must be saved. It's a special case.
|
|
*/
|
|
if (argNum != NO_ARG)
|
|
{
|
|
for (i = 0; i < *numPrompts; i++)
|
|
{
|
|
if ((*prompts)[i].argIndex == argNum)
|
|
return;
|
|
}
|
|
}
|
|
|
|
(*numPrompts)++;
|
|
*prompts = (PromptEntry *)XtRealloc((char *)*prompts,
|
|
(Cardinal)(sizeof(PromptEntry) * *numPrompts));
|
|
(*prompts)[(*numPrompts) - 1].argIndex = argNum;
|
|
(*prompts)[(*numPrompts) - 1].prompt = prompt;
|
|
}
|
|
|
|
|
|
/*
|
|
* This function takes an action DB entry and an action request, and
|
|
* determines if enough information was supplied to create the message
|
|
* needed to get the work done. If information was missing, then this
|
|
* function will return an array of prompt strings, which can be used
|
|
* to create a dialog for collecting the missing information. This
|
|
* function also returns an indication of how many of the parameters
|
|
* were left unused.
|
|
*
|
|
* The caller is responsible for freeing up the prompt array, but the
|
|
* entries in the array MUST NOT be freed up.
|
|
*/
|
|
|
|
static int
|
|
MatchParamsToAction(
|
|
ActionRequest *request,
|
|
int *numPrompts,
|
|
PromptEntry **prompts )
|
|
{
|
|
Boolean * paramUsed = NULL;
|
|
int unused;
|
|
Boolean argsOptionFound;
|
|
register int i;
|
|
int lastArgReferenced;
|
|
ActionPtr action = request->clonedAction;
|
|
|
|
/* Initialize things */
|
|
*numPrompts = 0;
|
|
*prompts = NULL;
|
|
argsOptionFound = False;
|
|
lastArgReferenced = -1;
|
|
|
|
/*
|
|
* This array lets us know which parameters can be used when we
|
|
* encounter the %Args% keyword.
|
|
*/
|
|
unused = request->numObjects;
|
|
if (unused > 0) {
|
|
paramUsed = (Boolean *)XtMalloc((Cardinal)(sizeof(Boolean) * unused));
|
|
for (i = 0; i < unused; i++)
|
|
paramUsed[i] = False;
|
|
}
|
|
|
|
if (IS_CMD(action->mask))
|
|
{
|
|
/*
|
|
* NOTE: The current implementation of prompt strings requires that
|
|
* the segments be evaluated in the same order in which the
|
|
* message fields were parsed.
|
|
* (See ResolveCommandInvokerMessagePieces() )
|
|
* This order is currently "execHost", "execString" and
|
|
* "termOpts". This situation arises because
|
|
* the existing prompt data structures do NOT identify the
|
|
* location of the prompt and hence where to put the
|
|
* user-supplied value; except by order of occurance.
|
|
*/
|
|
ProcessOneSegment(request, &(action->u.cmd.execHosts), prompts,
|
|
numPrompts, &argsOptionFound, &lastArgReferenced,
|
|
&unused, paramUsed);
|
|
ProcessOneSegment(request, &(action->u.cmd.execString), prompts,
|
|
numPrompts, &argsOptionFound, &lastArgReferenced,
|
|
&unused, paramUsed);
|
|
ProcessOneSegment(request, &(action->u.cmd.termOpts), prompts,
|
|
numPrompts, &argsOptionFound, &lastArgReferenced,
|
|
&unused, paramUsed);
|
|
}
|
|
else if (IS_TT_MSG(action->mask))
|
|
{
|
|
ProcessOneSegment(request, &(action->u.tt_msg.tt_op), prompts,
|
|
numPrompts, &argsOptionFound, &lastArgReferenced,
|
|
&unused, paramUsed);
|
|
ProcessOneSegment(request, &(action->u.tt_msg.tt_file), prompts,
|
|
numPrompts, &argsOptionFound, &lastArgReferenced,
|
|
&unused, paramUsed);
|
|
|
|
for (i = 0; i < action->u.tt_msg.vtype_count; i++)
|
|
{
|
|
ProcessOneSegment(request, &(action->u.tt_msg.tt_argn_vtype[i]),
|
|
prompts, numPrompts, &argsOptionFound,
|
|
&lastArgReferenced, &unused, paramUsed);
|
|
}
|
|
|
|
for (i = 0; i < action->u.tt_msg.value_count; i++)
|
|
{
|
|
/*
|
|
* We require that at most ONE argument be consumed by a
|
|
* tt_argn_value field.
|
|
*/
|
|
ProcessOneSegment(request, &(action->u.tt_msg.tt_argn_value[i]),
|
|
prompts, numPrompts, &argsOptionFound,
|
|
&lastArgReferenced, &unused, paramUsed);
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now that we have processed all of the pieces which will ultimately
|
|
* used to construct our message, determine if any of the arguments
|
|
* passed to _DtActionInvoke were not used; this allows us to tell
|
|
* the user that there were unused arguments, so they can choose
|
|
* to continue or abort the request.
|
|
* If we ever encountered a %Args% keyword, then ultimately all of
|
|
* the parameters will be used.
|
|
*/
|
|
if (argsOptionFound)
|
|
unused = 0;
|
|
else
|
|
{
|
|
/*
|
|
* Determine how many arguments were actually unused; only count
|
|
* those arguments AFTER the last referenced one. i.e. if arg2
|
|
* is referenced, but arg1 and arg3 are not, then only count arg3
|
|
* as an unused (and thus extra) parameter.
|
|
*/
|
|
for (i = 0; ((i < lastArgReferenced - 1) && (i < request->numObjects));
|
|
i++)
|
|
{
|
|
if (!paramUsed[i])
|
|
unused--;
|
|
}
|
|
|
|
/* This should never happen, but ... */
|
|
if (unused < 0)
|
|
unused = 0;
|
|
}
|
|
|
|
if (paramUsed) XtFree(paramUsed);
|
|
|
|
return(unused);
|
|
}
|
|
|
|
|
|
static void
|
|
ProcessOneSegment(
|
|
ActionRequest * request,
|
|
parsedMsg * msg,
|
|
PromptEntry **prompts,
|
|
int *numPrompts,
|
|
Boolean * argsOptionFound,
|
|
int * lastArgReferenced,
|
|
int * unused,
|
|
Boolean * paramUsed )
|
|
{
|
|
MsgComponent * piece;
|
|
int i;
|
|
|
|
/*
|
|
* Check each piece of this message component, to see if the parameter
|
|
* it expects has been supplied. If the parameter is missing, and
|
|
* a prompt was supplied, then add the prompt to the prompt array.
|
|
*/
|
|
for (i = 0; i < msg->numMsgParts; i++)
|
|
{
|
|
piece = msg->parsedMessage + i;
|
|
|
|
/*
|
|
* We only care about %Args% and %Arg_<n>% keywords, and
|
|
* entries which have no keyword, but do have a prompt.
|
|
*/
|
|
if (piece->keyword == ARG)
|
|
{
|
|
if (piece->argNum == ALL_ARGS)
|
|
{
|
|
/*
|
|
* When a %Args% keyword is found, this implies that there
|
|
* will ultimately be no unused parameters, because this
|
|
* keyword is replaced by all unused parameters.
|
|
*/
|
|
*argsOptionFound = True;
|
|
}
|
|
else if (piece->argNum > 0)
|
|
{
|
|
/* Keep track of the largest arg index referenced */
|
|
if (piece->argNum > *lastArgReferenced)
|
|
*lastArgReferenced = piece->argNum;
|
|
|
|
/* See if a parameter was supplied for this argNum */
|
|
if (piece->argNum > request->numObjects)
|
|
{
|
|
/* Parameter is missing; see if a prompt was given */
|
|
if (piece->prompt)
|
|
AddPrompt(piece->argNum, piece->prompt, numPrompts, prompts);
|
|
}
|
|
else
|
|
{
|
|
/* Mark this parameter as having been used */
|
|
if (!paramUsed[piece->argNum - 1])
|
|
{
|
|
paramUsed[piece->argNum - 1] = True;
|
|
(*unused)--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ((piece->keyword == NO_KEYWORD) && (piece->prompt))
|
|
{
|
|
/* Entries may be nothing but a prompt */
|
|
AddPrompt(NO_ARG, piece->prompt, numPrompts, prompts);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Prompt Dialog Support */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
|
|
/*
|
|
* This is the event handler which catches the 'escape' key when typed
|
|
* into the prompt. It will unpost the dialog.
|
|
*/
|
|
|
|
static void
|
|
CancelOut(
|
|
Widget w,
|
|
XEvent *event,
|
|
XtPointer params,
|
|
XtPointer num_params)
|
|
{
|
|
Arg args[10];
|
|
Widget cancel;
|
|
|
|
/* Get the cancel button widget id */
|
|
XtSetArg(args[0], XmNuserData, &cancel);
|
|
XtGetValues(w, args, 1);
|
|
|
|
/* Unpost the text annotation dialog */
|
|
XtCallCallbacks(cancel, XmNactivateCallback, NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'Cancel' callback for the dialog used to collect missing parameters
|
|
* from the user. It will free up the memory holding the cancelled
|
|
* request and will destroy the dialog.
|
|
*/
|
|
|
|
static void
|
|
CancelPromptDialog(
|
|
Widget widget,
|
|
PromptDialog *dialog,
|
|
XtPointer call_data )
|
|
|
|
{
|
|
unsigned long evalStatus;
|
|
unsigned long userStatus;
|
|
_DtActInvRecT *invp;
|
|
|
|
/* Destroy the dialog */
|
|
XtDestroyWidget(XtParent(dialog->topLevel));
|
|
|
|
/* Free up the prompt sub-structure */
|
|
XtFree((char *)dialog->prompts);
|
|
|
|
|
|
invp = _DtActFindInvRec(dialog->request->invocId);
|
|
myassert(invp); /* There should always be an invocation record */
|
|
|
|
/* Free up the original request structure */
|
|
_DtFreeRequest(dialog->request);
|
|
|
|
/* Free up the callback structure */
|
|
XtFree((char *)dialog);
|
|
|
|
if ( !invp )
|
|
return; /* This should never happen */
|
|
|
|
SET_INV_CANCEL(invp->state);
|
|
|
|
/*
|
|
* Evaluate whether we are done with this invocation -- are there
|
|
* uncompleted children? There should not be any subsequent invocations
|
|
* to worry about since this cancel effectively aborts further processing.
|
|
*
|
|
* We may have to return values to the caller.
|
|
*/
|
|
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
|
|
}
|
|
|
|
/*
|
|
* This function changes the focus from the given "widget's"
|
|
* tab group to the next tab group.
|
|
*/
|
|
|
|
static void
|
|
ChangePromptTraversal(
|
|
Widget widget,
|
|
register PromptDialog *dialog,
|
|
XtPointer call_data )
|
|
{
|
|
XmProcessTraversal (widget, XmTRAVERSE_NEXT_TAB_GROUP);
|
|
}
|
|
|
|
/*
|
|
* 'Ok' callback for the dialog used to collect missing parameters
|
|
* from the user. It will redo the array of parameter strings, and
|
|
* then execute the command, given whatever the user has supplied.
|
|
* It will also destroy the dialog box.
|
|
*/
|
|
|
|
static void
|
|
ProcessPromptDialog(
|
|
Widget widget,
|
|
register PromptDialog *dialog,
|
|
XtPointer call_data )
|
|
|
|
{
|
|
register int i, j;
|
|
String value;
|
|
|
|
/* Unpost the dialog */
|
|
XtUnmanageChild(dialog->topLevel);
|
|
|
|
/*
|
|
* Given the set of strings supplied by the user, update the
|
|
* object array which is part of the original request.
|
|
*/
|
|
for (i = 0; i < dialog->numPrompts; i++)
|
|
{
|
|
value = XmTextFieldGetString(dialog->prompts[i].promptWidget);
|
|
|
|
/* Do we need to grow the object array? */
|
|
if (dialog->prompts[i].argIndex > 0)
|
|
{
|
|
if (_DtEmptyString(value))
|
|
{
|
|
XtFree(value);
|
|
continue;
|
|
}
|
|
|
|
if (dialog->prompts[i].argIndex > dialog->request->numObjects)
|
|
{
|
|
dialog->request->objects = (ObjectData *)
|
|
XtRealloc((char *)dialog->request->objects,
|
|
(Cardinal)(sizeof(ObjectData) * (dialog->prompts[i].argIndex)));
|
|
|
|
/* Initialize the new array entries */
|
|
for (j = dialog->request->numObjects;
|
|
j < dialog->prompts[i].argIndex;
|
|
j++)
|
|
{
|
|
dialog->request->objects[j].mask = 0;
|
|
SET_FILE_OBJ(dialog->request->objects[j].mask);
|
|
dialog->request->objects[j].type = -1;
|
|
dialog->request->objects[j].u.file.hostIndex = -1;
|
|
dialog->request->objects[j].u.file.dirIndex = -1;
|
|
dialog->request->objects[j].u.file.origFilename = NULL;
|
|
dialog->request->objects[j].u.file.origHostname = NULL;
|
|
dialog->request->objects[j].u.file.baseFilename = NULL;
|
|
dialog->request->objects[j].u.file.bp = 0;
|
|
dialog->request->objects[j].u.file.sizebp = 0;
|
|
dialog->request->objects[j].u.buffer.bp = 0;
|
|
dialog->request->objects[j].u.buffer.size = 0;
|
|
dialog->request->objects[j].u.string.string = 0;
|
|
SET_UNKNOWN_IF_DIR(dialog->request->objects[j].mask);
|
|
}
|
|
dialog->request->numObjects = dialog->prompts[i].argIndex;
|
|
}
|
|
/*
|
|
* These values cannot be broken up into host/dir/file components,
|
|
* nor can they be typed, until we know it they refer to a file.
|
|
* This can't be determined until we construct the action message.
|
|
*/
|
|
dialog->request->objects[dialog->prompts[i].argIndex-1].u.file.
|
|
origFilename = value;
|
|
}
|
|
else /* Prompt-only input */
|
|
{
|
|
/*
|
|
* Prompt-only input can't fit in our ordered object array,
|
|
* since they don't have a unique argIndex which can be used
|
|
* as the index into the object array.
|
|
*/
|
|
dialog->request->numPromptInputs++;
|
|
dialog->request->promptInputs = (String *)
|
|
XtRealloc((char *)dialog->request->promptInputs,
|
|
(Cardinal)(dialog->request->numPromptInputs * sizeof(String)));
|
|
|
|
dialog->request->promptInputs[dialog->request->numPromptInputs-1] =
|
|
value;
|
|
}
|
|
}
|
|
|
|
/* Destroy the dialog */
|
|
XtDestroyWidget(XtParent(dialog->topLevel));
|
|
XmUpdateDisplay(widget);
|
|
|
|
/*
|
|
* Invoke the action using the information we've collected.
|
|
* If this was a single argument action, then the reprocessing
|
|
* of it may have generated another dialog, so we can only free
|
|
* up the request, when all processing is done.
|
|
*/
|
|
if (ProcessRequest(dialog->associatedWidget, dialog->request))
|
|
{
|
|
_DtActInvRecT *invp;
|
|
unsigned long evalStatus;
|
|
unsigned long userStatus;
|
|
|
|
if ( (invp = _DtActFindInvRec(dialog->request->invocId)) )
|
|
{
|
|
/* all done invoking ? */
|
|
RESET_INV_PENDING(invp->state);
|
|
|
|
/* We should only get here if all requests have been honored */
|
|
SET_INV_COMPLETE(invp->state);
|
|
|
|
/*
|
|
* evaluate whether all child actions have been completed
|
|
* and if its time to call the user callback.
|
|
*/
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
}
|
|
myassert(invp); /* there should always be one to find */
|
|
_DtFreeRequest(dialog->request);
|
|
}
|
|
|
|
/* Free up the prompt sub-structure */
|
|
XtFree((char *)dialog->prompts);
|
|
|
|
/* Free up the callback structure */
|
|
XtFree((char *)dialog);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function takes the array of prompt strings, and creates a
|
|
* dialog box, using these prompt strings as the labels for a set
|
|
* of text widgets.
|
|
*/
|
|
|
|
static void
|
|
CreatePromptDialog(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
register int numPrompts,
|
|
register PromptEntry *prompts )
|
|
|
|
{
|
|
register PromptDialog * dialog;
|
|
register DialogPromptEntry * promptDes;
|
|
XmString pt1;
|
|
String title;
|
|
Widget shell, bboard, frame, form, label;
|
|
Widget promptLabel, topAttach;
|
|
Widget separator, ok, cancel;
|
|
register int count;
|
|
int n, i;
|
|
Arg args[20];
|
|
XmString labelString;
|
|
XWindowAttributes xwa;
|
|
Status status;
|
|
Boolean is_mapped = False;
|
|
static XtTranslations trans_table;
|
|
static Boolean first = True;
|
|
Atom xa_WM_DELETE_WINDOW;
|
|
|
|
/*
|
|
* Want to set up the Escape key so that it will unpost the dialog.
|
|
*/
|
|
_DtSvcProcessLock();
|
|
if (first)
|
|
{
|
|
XtAppAddActions(XtWidgetToApplicationContext(w), actionTable, 1);
|
|
trans_table = XtParseTranslationTable(translations_escape);
|
|
first = False;
|
|
}
|
|
_DtSvcProcessUnlock();
|
|
|
|
/* Allocate the structures we'll be needing */
|
|
dialog = (PromptDialog *)XtMalloc((Cardinal)sizeof(PromptDialog));
|
|
promptDes = (DialogPromptEntry *)XtMalloc((Cardinal)
|
|
(sizeof(DialogPromptEntry) * numPrompts));
|
|
|
|
|
|
/* Create the shell, frame and form used for the dialog. */
|
|
|
|
title = (char *)XtMalloc((Cardinal)
|
|
(strlen(PromptDialogTitle)+ strlen(request->clonedAction->label) + 1));
|
|
(void)sprintf(title, "%1$s%2$s", PromptDialogTitle, request->clonedAction->label);
|
|
n = 0;
|
|
XtSetArg (args[n], XmNallowShellResize, True); n++;
|
|
XtSetArg (args[n], XmNtitle, title); n++;
|
|
shell = XmCreateDialogShell (w, "promptDialog", args, n);
|
|
XtFree(title);
|
|
|
|
if (XtIsRealized(w))
|
|
{
|
|
status = XGetWindowAttributes (XtDisplay (w), XtWindow (w), &xwa);
|
|
if (status && (xwa.map_state == IsViewable))
|
|
is_mapped = True;
|
|
}
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNmarginWidth, 0); n++;
|
|
XtSetArg (args[n], XmNmarginHeight, 0); n++;
|
|
if (!is_mapped)
|
|
{
|
|
XtSetArg (args[n], XmNdefaultPosition, False);
|
|
n++;
|
|
}
|
|
bboard = XmCreateBulletinBoard (shell, "bboard", args, n);
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNshadowThickness, 1); n++;
|
|
XtSetArg (args[n], XmNshadowType, XmSHADOW_OUT); n++;
|
|
frame = XmCreateFrame (bboard, "frame", args, n);
|
|
XtManageChild (frame);
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNautoUnmanage, False); n++;
|
|
XtSetArg (args[n], XmNtextTranslations, trans_table); n++;
|
|
form = XmCreateForm (frame, "form", args, n);
|
|
XtManageChild (form);
|
|
|
|
/* Create the dialog description label */
|
|
|
|
pt1 = XmStringCreateLocalized(PromptDialogLabel);
|
|
n = 0;
|
|
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg(args[n], XmNleftOffset, 20); n++;
|
|
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg(args[n], XmNrightOffset, 20); n++;
|
|
XtSetArg(args[n], XmNtopOffset, 15); n++;
|
|
XtSetArg(args[n], XmNlabelString, pt1); n++;
|
|
label = XmCreateLabelGadget(form, "label", args, n);
|
|
XtManageChild (label);
|
|
XmStringFree(pt1);
|
|
|
|
/* Create each of the needed prompts */
|
|
topAttach = label;
|
|
for (count = 0; count < numPrompts; count++)
|
|
{
|
|
promptDes[count].argIndex = prompts[count].argIndex;
|
|
|
|
pt1 = XmStringCreateLocalized(prompts[count].prompt);
|
|
n = 0;
|
|
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
|
|
XtSetArg(args[n], XmNtopWidget, topAttach); n++;
|
|
XtSetArg(args[n], XmNtopOffset, 10); n++;
|
|
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg(args[n], XmNleftOffset, 30); n++;
|
|
XtSetArg(args[n], XmNlabelString, pt1); n++;
|
|
promptLabel = XmCreateLabelGadget(form, "promptLabel", args, n);
|
|
XtManageChild(promptLabel);
|
|
XmStringFree(pt1);
|
|
|
|
n = 0;
|
|
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
|
|
XtSetArg(args[n], XmNtopWidget, topAttach); n++;
|
|
XtSetArg(args[n], XmNtopOffset, 8); n++;
|
|
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg(args[n], XmNrightOffset, 30); n++;
|
|
XtSetArg(args[n], XmNtraversalOn, True); n++;
|
|
XtSetArg(args[n], XmNleftAttachment,XmATTACH_WIDGET ); n++;
|
|
XtSetArg(args[n], XmNleftWidget, promptLabel); n++;
|
|
XtSetArg(args[n], XmNleftOffset, 15); n++;
|
|
promptDes[count].promptWidget = XmCreateTextField(form, "text", args, n);
|
|
|
|
XtManageChild(promptDes[count].promptWidget);
|
|
|
|
XmAddTabGroup(promptDes[count].promptWidget);
|
|
topAttach = promptDes[count].promptWidget;
|
|
}
|
|
|
|
|
|
/* Create a separator between the buttons */
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
|
|
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
|
|
XtSetArg (args[n], XmNtopWidget, topAttach); n++;
|
|
XtSetArg (args[n], XmNtopOffset, 20); n++;
|
|
separator = XmCreateSeparatorGadget (form, "separator", args, n);
|
|
XtManageChild (separator);
|
|
|
|
|
|
/* Create the ok and cancel buttons */
|
|
|
|
n = 0;
|
|
labelString = XmStringCreateLocalized((String)_DtOkString);
|
|
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
|
|
XtSetArg (args[n], XmNleftPosition, 5 + 10); n++;
|
|
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
|
|
XtSetArg (args[n], XmNrightPosition, 31 + 10); n++;
|
|
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
|
|
XtSetArg (args[n], XmNtopWidget, separator); n++;
|
|
XtSetArg (args[n], XmNtopOffset, 16); n++;
|
|
XtSetArg (args[n], XmNbottomOffset, 16); n++;
|
|
XtSetArg (args[n], XmNmarginHeight, 4); n++;
|
|
XtSetArg (args[n], XmNshowAsDefault, True); n++;
|
|
XtSetArg (args[n], XmNlabelString, labelString); n++;
|
|
ok = XmCreatePushButtonGadget (form, "ok", args, n);
|
|
XtManageChild(ok);
|
|
XtAddCallback(ok, XmNactivateCallback, (XtCallbackProc)ProcessPromptDialog,
|
|
(XtPointer)dialog);
|
|
XmStringFree(labelString);
|
|
|
|
/* Set the default action */
|
|
|
|
if (numPrompts <= 1)
|
|
{
|
|
n = 0;
|
|
XtSetArg (args[n], XmNdefaultButton, ok); n++;
|
|
XtSetValues(bboard, args, n);
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
/*
|
|
* Want to set the traversal so that if "return" is hit in the
|
|
* last prompt, the "ProcessPromptDialog" callback is invoked.
|
|
* Otherwise, the "return" should move the focus to the next
|
|
* which is in a different (tab group).
|
|
*/
|
|
for (i = 0; i < numPrompts; i++)
|
|
{
|
|
if (i <= (numPrompts - 2))
|
|
XtAddCallback(promptDes[i].promptWidget, XmNactivateCallback,
|
|
(XtCallbackProc)ChangePromptTraversal,
|
|
(XtPointer)dialog);
|
|
else
|
|
XtAddCallback(promptDes[i].promptWidget, XmNactivateCallback,
|
|
(XtCallbackProc)ProcessPromptDialog,
|
|
(XtPointer)dialog);
|
|
}
|
|
}
|
|
|
|
n = 0;
|
|
labelString = XmStringCreateLocalized((String)_DtCancelString);
|
|
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
|
|
XtSetArg (args[n], XmNleftPosition, 37 + 22); n++;
|
|
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
|
|
XtSetArg (args[n], XmNrightPosition, 63 + 22); n++;
|
|
XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
|
|
XtSetArg (args[n], XmNtopWidget, separator); n++;
|
|
XtSetArg (args[n], XmNtopOffset, 21); n++;
|
|
XtSetArg (args[n], XmNbottomOffset, 21); n++;
|
|
XtSetArg (args[n], XmNmarginHeight, 4); n++;
|
|
XtSetArg (args[n], XmNlabelString, labelString); n++;
|
|
cancel = XmCreatePushButtonGadget (form, "cancel", args, n);
|
|
XtManageChild(cancel);
|
|
XtAddCallback(cancel, XmNactivateCallback, (XtCallbackProc)CancelPromptDialog,
|
|
(XtPointer)dialog);
|
|
XmStringFree(labelString);
|
|
|
|
/*
|
|
* For each prompt, must set up the Escape key to be equivalent
|
|
* to the "Cancel button.
|
|
*/
|
|
for (i = 0; i < numPrompts; i++) {
|
|
n = 0;
|
|
XtSetArg(args[n], XmNuserData, cancel); n++;
|
|
XtSetValues(promptDes[i].promptWidget, args, n);
|
|
}
|
|
|
|
/*
|
|
* If the widget is not mapped, center this dialog.
|
|
*/
|
|
if (!is_mapped)
|
|
{
|
|
Dimension dialogWd, dialogHt;
|
|
|
|
XtSetArg(args[0], XmNmappedWhenManaged, False);
|
|
XtSetValues(shell, args, 1);
|
|
|
|
XtManageChild(bboard);
|
|
XtRealizeWidget(shell);
|
|
|
|
XtSetArg(args[0], XmNwidth, &dialogWd);
|
|
XtSetArg(args[1], XmNheight, &dialogHt);
|
|
XtGetValues(bboard, args, 2);
|
|
|
|
XtSetArg (args[0], XmNx,
|
|
(WidthOfScreen(XtScreen(bboard)) - dialogWd) / 2U);
|
|
XtSetArg (args[1], XmNy,
|
|
(HeightOfScreen(XtScreen(bboard)) - dialogHt) / 2U);
|
|
XtSetValues (bboard, args, 2);
|
|
}
|
|
|
|
/* Adjust the decorations for the dialog shell of the dialog */
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNmwmDecorations,
|
|
MWM_DECOR_BORDER | MWM_DECOR_MENU | MWM_DECOR_TITLE); n++;
|
|
XtSetValues(shell, args, n);
|
|
|
|
xa_WM_DELETE_WINDOW =
|
|
XInternAtom(XtDisplay(shell), "WM_DELETE_WINDOW", False);
|
|
XmAddWMProtocolCallback(
|
|
shell, xa_WM_DELETE_WINDOW,
|
|
(XtCallbackProc) CancelPromptDialog, (XtPointer) dialog);
|
|
|
|
/* Fill in our instance structure */
|
|
dialog->request = request;
|
|
dialog->topLevel = bboard;
|
|
dialog->numPrompts = count;
|
|
dialog->prompts = promptDes;
|
|
dialog->associatedWidget = w;
|
|
|
|
/* Post the dialog */
|
|
XtSetArg(args[0], XmNmappedWhenManaged, True);
|
|
XtSetValues(shell, args, 1);
|
|
XtManageChild(bboard);
|
|
|
|
/* Make the first prompt automatically get the focus. */
|
|
if (numPrompts >= 0)
|
|
XmProcessTraversal(promptDes[0].promptWidget, XmTRAVERSE_CURRENT);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Continue Dialog Support */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
|
|
/*
|
|
* 'Ok' callback for the abort/continue dialog. It will continue with the
|
|
* processing of the request, ignoring any unused parameters.
|
|
*/
|
|
|
|
static void
|
|
ContinueRequest(
|
|
Widget widget,
|
|
XtPointer user_data,
|
|
XtPointer call_data )
|
|
|
|
{
|
|
register int i;
|
|
register ContinueDialog *dialog = (ContinueDialog *)user_data;
|
|
|
|
/* Destroy the dialog */
|
|
XtDestroyWidget(XtParent(dialog->topLevel));
|
|
XmUpdateDisplay(widget);
|
|
|
|
/*
|
|
* If we need to collect some prompt input from the user, then
|
|
* post the prompt dialog; otherwise, send the action request.
|
|
*/
|
|
if (dialog->numPrompts == 0)
|
|
{
|
|
if (ProcessRequest(dialog->associatedWidget, dialog->request))
|
|
{
|
|
_DtActInvRecT *invp;
|
|
|
|
if((invp=_DtActFindInvRec(dialog->request->invocId))!=NULL)
|
|
{
|
|
/* all done invoking ? */
|
|
RESET_INV_PENDING(invp->state);
|
|
|
|
/* We should only get here if all requests have been honored */
|
|
SET_INV_COMPLETE(invp->state);
|
|
|
|
/*
|
|
* evaluate whether all child actions have been completed
|
|
* and if its time to call the user callback.
|
|
*/
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
}
|
|
|
|
myassert(invp); /* there should always be one to find */
|
|
_DtFreeRequest(dialog->request);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreatePromptDialog(dialog->associatedWidget, dialog->request,
|
|
dialog->numPrompts,
|
|
dialog->prompts);
|
|
}
|
|
|
|
|
|
/* Free up the prompt sub-structure */
|
|
for (i = 0; i < dialog->numPrompts; i++)
|
|
XtFree(dialog->prompts[i].prompt);
|
|
XtFree((char *)dialog->prompts);
|
|
|
|
/* Free up the callback structure */
|
|
XtFree((char *)dialog);
|
|
}
|
|
|
|
|
|
/*
|
|
* 'Cancel' callback for the dialog which prompts the user to continue
|
|
* or abort, when too many parameters have been supplied. This will
|
|
* free up the dialog data and the request and will destroy the dialog.
|
|
*/
|
|
|
|
static void
|
|
CancelRequest(
|
|
Widget widget,
|
|
XtPointer user_data,
|
|
XtPointer call_data )
|
|
|
|
{
|
|
register int i;
|
|
register ContinueDialog *dialog = (ContinueDialog *)user_data;
|
|
unsigned long evalStatus;
|
|
unsigned long userStatus;
|
|
_DtActInvRecT *invp;
|
|
|
|
/* Destroy the dialog */
|
|
XtDestroyWidget(XtParent(dialog->topLevel));
|
|
|
|
/* Free up the prompt sub-structure */
|
|
for (i = 0; i < dialog->numPrompts; i++)
|
|
XtFree(dialog->prompts[i].prompt);
|
|
XtFree((char *)dialog->prompts);
|
|
|
|
/* get the invocation record */
|
|
invp = _DtActFindInvRec(dialog->request->invocId);
|
|
myassert(invp); /* There should always be one available */
|
|
|
|
/* Free up the original request structure */
|
|
_DtFreeRequest(dialog->request);
|
|
|
|
/* Free up the callback structure */
|
|
XtFree((char *)dialog);
|
|
|
|
if ( !invp )
|
|
return; /* should never happen */
|
|
|
|
SET_INV_CANCEL(invp->state);
|
|
/*
|
|
* Evaluate whether we are done with this invocation -- are there
|
|
* uncompleted children? There should not be any subsequent invocations
|
|
* to worry about since this cancel effectively aborts further processing.
|
|
*
|
|
* We may have to return values to the caller.
|
|
*/
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
}
|
|
|
|
/*
|
|
* When an action is requested, and more parameters than are needed
|
|
* are supplied, the user will be prompted to continue with the
|
|
* operation (ignoring the extra parameters), or to abort the request.
|
|
*
|
|
* This function builds the dialog which will collect the user's response.
|
|
*/
|
|
|
|
static void
|
|
CreateContinueDialog(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
int numPrompts,
|
|
register PromptEntry *prompts )
|
|
|
|
{
|
|
register ContinueDialog * dialog;
|
|
String title;
|
|
XmString label;
|
|
register int i;
|
|
int n;
|
|
Arg args[10];
|
|
XmString ok, cancel;
|
|
char *fmt;
|
|
|
|
/* Allocate the structures we'll be needing */
|
|
dialog = (ContinueDialog *)XtMalloc((Cardinal)sizeof(ContinueDialog));
|
|
dialog->request = request;
|
|
dialog->associatedWidget = w;
|
|
dialog->numPrompts = numPrompts;
|
|
|
|
/*
|
|
* We need to make a clone of the prompt array, since the strings
|
|
* it contains are not ones we can guarantee will be around when
|
|
* the user finally responds to this dialog.
|
|
*/
|
|
if (prompts)
|
|
{
|
|
dialog->prompts = (PromptEntry *)
|
|
XtMalloc((Cardinal)(sizeof(PromptEntry) * numPrompts));
|
|
for (i = 0; i < numPrompts; i++)
|
|
{
|
|
dialog->prompts[i].argIndex = prompts[i].argIndex;
|
|
dialog->prompts[i].prompt = XtNewString(prompts[i].prompt);
|
|
}
|
|
}
|
|
else
|
|
dialog->prompts = NULL;
|
|
|
|
ok = XmStringCreateLocalized((String)_DtOkString);
|
|
cancel = XmStringCreateLocalized((String)_DtCancelString);
|
|
|
|
/* Create the error dialog */
|
|
fmt = XtNewString((char *)Dt11GETMESSAGE(2, 2, "%1$s%2$s"));
|
|
title = (char *)XtMalloc((Cardinal)
|
|
(strlen(PromptDialogTitle) +
|
|
strlen(request->clonedAction->label) +
|
|
strlen(fmt) + 1));
|
|
(void)sprintf(title, fmt, PromptDialogTitle, request->clonedAction->label);
|
|
label = XmStringCreateLocalized(ContinueMessage);
|
|
XtFree(fmt);
|
|
|
|
n = 0;
|
|
XtSetArg(args[n], XmNmessageString, label); n++;
|
|
XtSetArg(args[n], XmNtitle, title); n++;
|
|
XtSetArg(args[n], XmNokLabelString, ok); n++;
|
|
XtSetArg(args[n], XmNcancelLabelString, cancel); n++;
|
|
dialog->topLevel = XmCreateWarningDialog(w, "continueDialog", args, n);
|
|
XtFree(title);
|
|
XmStringFree(ok);
|
|
XmStringFree(cancel);
|
|
XmStringFree(label);
|
|
|
|
XtUnmanageChild(XmMessageBoxGetChild(dialog->topLevel,
|
|
XmDIALOG_HELP_BUTTON));
|
|
XtAddCallback(dialog->topLevel, XmNokCallback, ContinueRequest,
|
|
(XtPointer)dialog);
|
|
XtAddCallback(dialog->topLevel, XmNcancelCallback, CancelRequest,
|
|
(XtPointer)dialog);
|
|
XtManageChild(dialog->topLevel);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
/* Command Invoker Specific Functions */
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
|
|
/*
|
|
* This is the entry point into the command-specific world. All of the code
|
|
* before this has been written to handle any of the different transport
|
|
* types. The code from this point on will know specifically how to
|
|
* interact with the command invoker layer. We will start by taking each
|
|
* of the pieces of information making up the command invoker request, and
|
|
* resolving any of the keywords, by replacing them with the appropriate
|
|
* information. If this fails (which it only should do if we try to map
|
|
* a file to a host which cannot be accessed), then we will either continue,
|
|
* using the next exec host, or we will terminate, if no more hosts are left.
|
|
*/
|
|
|
|
static void
|
|
ProcessCommandInvokerRequest(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir )
|
|
|
|
{
|
|
char * cwdHost;
|
|
char * cwdDir;
|
|
ActionPtr action = request->clonedAction;
|
|
_DtActInvRecT *invp = NULL;
|
|
_DtActChildRecT *childp = NULL;
|
|
|
|
if (ResolveCommandInvokerMessagePieces(w, request, relPathHost, relPathDir))
|
|
{
|
|
/*
|
|
* Issue the request; the success/failure notification comes
|
|
* asynchronously; that's when everything gets cleaned up, or
|
|
* tried again, for the next exec host.
|
|
*/
|
|
__ExtractCWD(request, &cwdHost, &cwdDir, True);
|
|
InitiateCommandInvokerRequest( w, request, cwdHost, cwdDir);
|
|
XtFree(cwdHost);
|
|
XtFree(cwdDir);
|
|
}
|
|
else
|
|
{
|
|
if ( !(invp = _DtActFindInvRec(request->invocId) ) )
|
|
myassert( 0 /* could not find invocation record */ );
|
|
|
|
if ( !(childp=_DtActFindChildRec(request->invocId,request->childId)))
|
|
myassert( 0 /* could not find child record */ );
|
|
|
|
/*
|
|
* The only way we could have reached here is if the execution host
|
|
* was not accessible, and we tried to map one of the data files to
|
|
* be relative to this host. If there are other hosts to be tried,
|
|
* then we will retry the request on the next host; otherwise, we
|
|
* will post an error dialog, and bail out.
|
|
*/
|
|
|
|
request->hostIndex++;
|
|
|
|
if (request->hostIndex >= action->u.cmd.execHostCount)
|
|
{
|
|
/* No more hosts to try; report an error, and bail out */
|
|
|
|
if ( invp && childp )
|
|
{
|
|
SET_INV_ERROR(invp->state);
|
|
childp->childState = _DtActCHILD_FAILED;
|
|
}
|
|
|
|
/*
|
|
* Cleanup should happen later when we return up the stack.
|
|
*/
|
|
|
|
if (action->u.cmd.execHostCount <= 1)
|
|
{
|
|
/* Display error dialog listing just the one failed exec host */
|
|
HostAccessError(w, request->clonedAction->label, request->badHostList);
|
|
}
|
|
else
|
|
{
|
|
/* Display error dialog listing all failed exec hosts */
|
|
MultiHostAccessError(w, request->clonedAction->label, request->badHostList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( invp && childp )
|
|
{
|
|
/*
|
|
* Delete child record for failed exec on this host
|
|
*/
|
|
_DtActDeleteChildRec(invp,childp);
|
|
SET_INV_PENDING(invp->state);
|
|
if ( ! invp->numChildren )
|
|
RESET_INV_WORKING(invp->state);
|
|
}
|
|
|
|
/* Retry the request, using the next exec host */
|
|
PrepareAndExecuteAction(w, request);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This function takes all of the pieces making up a command invoker request,
|
|
* and resolves any references to keywords, using both the passed-in
|
|
* arguments, and any information collected from the prompt dialog.
|
|
*/
|
|
|
|
static Boolean
|
|
ResolveCommandInvokerMessagePieces(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
char * relPathHost,
|
|
char * relPathDir )
|
|
|
|
{
|
|
ActionPtr action = request->clonedAction;
|
|
cmdAttr * cmd = &(action->u.cmd);
|
|
char * termOpts;
|
|
Boolean * paramUsed = NULL;
|
|
int promptDataIndex = 0;
|
|
/*
|
|
* NOTE: The current implementation of prompt strings requires that
|
|
* the segments be evaluated in the same order in which the
|
|
* action fields were parsed. (See MatchParamsToAction() )
|
|
* This order is currently "execHost", "execString" and
|
|
* "termOpts". This situation arises because
|
|
* the existing prompt data structures do NOT identify the
|
|
* location of the prompt and hence where to put the
|
|
* user-supplied value; except by order of occurance.
|
|
*/
|
|
|
|
/* Set up the next host to execute on */
|
|
_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
|
|
&(action->u.cmd.execHosts), True, 0, ¶mUsed,
|
|
&promptDataIndex);
|
|
SetExecHost(request);
|
|
|
|
if ((_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
|
|
&(cmd->execString), False, 0, ¶mUsed,
|
|
&promptDataIndex) == False) ||
|
|
(_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
|
|
&(cmd->termOpts), False, 0, ¶mUsed,
|
|
&promptDataIndex) == False))
|
|
{
|
|
/* Free up any intermediate work we've done here */
|
|
XtFree(cmd->execString.compiledMessage);
|
|
XtFree(cmd->termOpts.compiledMessage);
|
|
XtFree(cmd->execHosts.compiledMessage);
|
|
cmd->execString.compiledMessage = NULL;
|
|
cmd->termOpts.compiledMessage = NULL;
|
|
cmd->execHosts.compiledMessage = NULL;
|
|
XtFree(paramUsed);
|
|
return(False);
|
|
}
|
|
|
|
/*
|
|
* If term_opts were passed in to the _DtActionInvoke() function, then
|
|
* append them to the term_opts derived from the action definition and
|
|
* internal defaults. This should give precedence to the last defined
|
|
* options.
|
|
*/
|
|
if ( request->termOpts )
|
|
{
|
|
termOpts = XtMalloc( strlen(cmd->termOpts.compiledMessage) +
|
|
strlen(request->termOpts) + 2 );
|
|
strcpy(termOpts,cmd->termOpts.compiledMessage);
|
|
strcat(termOpts," ");
|
|
strcat(termOpts,request->termOpts);
|
|
XtFree(cmd->termOpts.compiledMessage);
|
|
cmd->termOpts.compiledMessage = termOpts;
|
|
}
|
|
|
|
XtFree(paramUsed);
|
|
return(True);
|
|
}
|
|
|
|
|
|
/*
|
|
* Process a command-invoker request.
|
|
*/
|
|
|
|
static void
|
|
InitiateCommandInvokerRequest(
|
|
Widget w,
|
|
ActionRequest *request,
|
|
String host,
|
|
String dir)
|
|
|
|
{
|
|
char procIdBuf[_DtAct_MAX_BUF_SIZE];
|
|
char tmpFileBuf[_DtAct_MAX_BUF_SIZE];
|
|
char *procId; /* for dtexec command line */
|
|
char *tmpFiles = NULL; /* for dtexec command line */
|
|
_DtActInvRecT *invp;
|
|
_DtActChildRecT *childp;
|
|
CallbackData *data=(CallbackData *)XtMalloc((Cardinal)sizeof(CallbackData));
|
|
ActionPtr action = request->clonedAction;
|
|
|
|
tmpFileBuf[0]='\0'; /* seed the buffer with a null string */
|
|
|
|
/*
|
|
* Generate the procId option string for dtexec
|
|
*/
|
|
|
|
/* Get the default procId from toolTalk */
|
|
switch ( tt_ptr_error(procId = tt_default_procid()) )
|
|
{
|
|
case TT_ERR_NOMP:
|
|
; /* fall through */
|
|
case TT_ERR_PROCID: /* Try to establish a connection */
|
|
tt_free(procId) ;
|
|
if ( !_DtInitializeToolTalk(NULL) )
|
|
procId=NULL;
|
|
else if ( tt_ptr_error(procId = tt_default_procid()) != TT_OK )
|
|
{
|
|
myassert( 0 ); /* we should never get here */
|
|
procId = NULL;
|
|
}
|
|
break;
|
|
case TT_OK:
|
|
break;
|
|
default:
|
|
tt_free(procId);
|
|
procId = NULL;
|
|
break;
|
|
}
|
|
/*
|
|
* The string generated for procId should never exceed the procId buf size.
|
|
*/
|
|
sprintf(procIdBuf,"%s_%d_%lu",
|
|
_DtActNULL_GUARD(procId),
|
|
(int) request->invocId,
|
|
request->childId );
|
|
|
|
myassert( strlen(procIdBuf) < sizeof(procIdBuf) );
|
|
|
|
if (procId)
|
|
tt_free(procId);
|
|
procId = procIdBuf; /* no need to malloc */
|
|
|
|
/*
|
|
* Generate string of tmp file args for dtexec.
|
|
*/
|
|
|
|
if ( (invp = _DtActFindInvRec(request->invocId)) != NULL )
|
|
{
|
|
if ( (childp =
|
|
_DtActFindChildRec(request->invocId,request->childId)) != NULL )
|
|
{
|
|
int i;
|
|
char *p;
|
|
int len = 0;
|
|
|
|
for(i = 0; i < childp->numObjects; i++)
|
|
{
|
|
|
|
if(!(IS_BUFFER_OBJ(invp->info[childp->argMap[i].argIdx].mask)))
|
|
continue; /* not a buffer object */
|
|
|
|
if ( !(p = invp->info[childp->argMap[i].argIdx].name) )
|
|
continue; /* no tmp file name */
|
|
|
|
/* Add up the string length of the file name */
|
|
if((len += strlen(" -tmp ") + strlen(p)) < sizeof(tmpFileBuf))
|
|
{
|
|
/*
|
|
* Use the automatic tmpFileBuf if possible
|
|
*/
|
|
strcat(tmpFileBuf," -tmp ");
|
|
strcat(tmpFileBuf,p);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Malloc more space if necessary
|
|
*/
|
|
XtFree(tmpFiles);
|
|
tmpFiles = XtMalloc(len + 1);
|
|
strcpy(tmpFiles,tmpFileBuf);
|
|
strcpy(tmpFiles," -tmp ");
|
|
strcpy(tmpFiles,p);
|
|
}
|
|
|
|
}
|
|
if ( len > 0 && len < sizeof(tmpFileBuf) )
|
|
tmpFiles = tmpFileBuf;
|
|
}
|
|
else
|
|
{
|
|
myassert( 0 /* could not find child rec */ );
|
|
tmpFiles = NULL;
|
|
}
|
|
}
|
|
else
|
|
tmpFiles = NULL;
|
|
|
|
/* Fill out the callback structure */
|
|
data->actionLabel = XtNewString(request->clonedAction->label);
|
|
data->associatedWidget = w;
|
|
data->offset = 0;
|
|
data->actionPtr = action;
|
|
data->requestPtr = _DtCloneRequest(request);
|
|
|
|
|
|
if ( _DtActionCommandInvoke(action->mask & _DtAct_WINTYPE_BITS, host, dir,
|
|
action->u.cmd.execString.compiledMessage,
|
|
action->u.cmd.termOpts.compiledMessage,
|
|
request->currentHost,
|
|
procId,
|
|
tmpFiles,
|
|
CmdInvSuccessfulRequest, (XtPointer)data,
|
|
CmdInvFailedRequest, (XtPointer)data) )
|
|
if (invp)
|
|
SET_INV_CMD_QUEUED(invp->state);
|
|
|
|
|
|
if ( tmpFiles != tmpFileBuf )
|
|
XtFree(tmpFiles);
|
|
}
|
|
|
|
|
|
/*
|
|
* Sets the 'currentHost' field within the request structure to
|
|
* the name of the next exec host to try. Before any of the exec hosts
|
|
* in the action definition are tried, we will try the host passed in as
|
|
* part of the request, if one was specified.
|
|
*/
|
|
|
|
static void
|
|
SetExecHost (
|
|
ActionRequest * request )
|
|
|
|
{
|
|
ActionPtr action = request->clonedAction;
|
|
int hostCount = 0;
|
|
int hostListSize = 0;
|
|
char ** hostList = NULL;
|
|
|
|
XtFree(request->currentHost);
|
|
|
|
/* If this is the first call, we may need to parse the host list */
|
|
if (action->u.cmd.execHostArray == NULL)
|
|
{
|
|
/* Explicitly specified execHost overrides action definition execHost */
|
|
if (request->execHost)
|
|
ParseHostList(request->execHost, &hostList, &hostListSize, &hostCount);
|
|
else if (action->u.cmd.execHosts.compiledMessage)
|
|
{
|
|
ParseHostList(action->u.cmd.execHosts.compiledMessage, &hostList,
|
|
&hostListSize, &hostCount);
|
|
}
|
|
|
|
RemoveDuplicateHostNames(hostList, &hostCount);
|
|
|
|
action->u.cmd.execHostArray = hostList;
|
|
action->u.cmd.execHostCount = hostCount;
|
|
}
|
|
|
|
if (action->u.cmd.execHostCount == 0)
|
|
{
|
|
/*
|
|
* Oh boy ... someone is trying to be nasty! The only way we could
|
|
* have gotten here was to have the action's 'EXEC_HOST' field set
|
|
* to nothing but a prompt string, and then the user left the string
|
|
* empty! We'll default the local host, and hope it works.
|
|
*/
|
|
/*
|
|
* fdt: we really should default to whatever has been configured
|
|
* as the default execution host, followed by the LocalHost,
|
|
* if they are not the same.
|
|
*/
|
|
request->currentHost = _DtGetLocalHostName();
|
|
}
|
|
else
|
|
{
|
|
request->currentHost =
|
|
XtNewString(action->u.cmd.execHostArray[request->hostIndex]);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* This function takes a string of comma-separated host names, and adds them
|
|
* to the passed-in string array.
|
|
*/
|
|
|
|
static void
|
|
ParseHostList (
|
|
char * hostString,
|
|
char *** hostListPtr,
|
|
int * hostListSizePtr,
|
|
int * hostCountPtr )
|
|
|
|
{
|
|
char * workString;
|
|
char * nextHost;
|
|
_Xstrtokparams strtok_buf;
|
|
|
|
workString = XtNewString(hostString);
|
|
nextHost = _XStrtok(workString, ",", strtok_buf);
|
|
|
|
while(nextHost)
|
|
{
|
|
nextHost = _DtStripSpaces(nextHost);
|
|
|
|
if (strlen(nextHost) > 0)
|
|
{
|
|
if (*hostCountPtr >= *hostListSizePtr)
|
|
{
|
|
(*hostListSizePtr) += 5;
|
|
(*hostListPtr) = (char **)XtRealloc((char *)(*hostListPtr),
|
|
sizeof(char *) * (*hostListSizePtr));
|
|
}
|
|
|
|
(*hostListPtr)[*hostCountPtr] = XtNewString(nextHost);
|
|
(*hostCountPtr)++;
|
|
}
|
|
|
|
nextHost = _XStrtok(NULL, ",", strtok_buf);
|
|
}
|
|
|
|
XtFree(workString);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function goes through the compiled list of exec hosts, and removes
|
|
* any duplicate entries. It is not very useful to attempt to execute on
|
|
* a given host, more than once.
|
|
*/
|
|
static void
|
|
RemoveDuplicateHostNames (
|
|
char ** hostList,
|
|
int * hostCountPtr )
|
|
|
|
{
|
|
int i,j,k;
|
|
|
|
for (i = 0; i < *hostCountPtr; i++)
|
|
{
|
|
for (j = i+1; j < *hostCountPtr; )
|
|
{
|
|
if (strcmp(hostList[i], hostList[j]) == 0)
|
|
{
|
|
/* Remove the second entry */
|
|
XtFree(hostList[j]);
|
|
for (k = j; k < (*hostCountPtr) - 1; k++)
|
|
hostList[k] = hostList[k+1];
|
|
(*hostCountPtr)--;
|
|
}
|
|
else
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* When one of the exec hosts fails, we add it to the list of failed
|
|
* hostnames, so that if ultimately all of the hosts fail, we have a
|
|
* list we can display within the error dialog.
|
|
*/
|
|
|
|
static void
|
|
AddFailedHostToList (
|
|
ActionRequest * request,
|
|
String badHost )
|
|
|
|
{
|
|
int curLen;
|
|
|
|
if (request->badHostList)
|
|
curLen = strlen(request->badHostList);
|
|
else
|
|
curLen = 0;
|
|
|
|
request->badHostList = XtRealloc(request->badHostList,
|
|
curLen + 10 + strlen(badHost));
|
|
|
|
if (curLen > 0)
|
|
{
|
|
strcat(request->badHostList, ", ");
|
|
strcat(request->badHostList, badHost);
|
|
}
|
|
else
|
|
strcpy(request->badHostList, badHost);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* This callback is invoked when the Command Invoker library has successfully
|
|
* exectued an action. We need to free up everything associated with this
|
|
* request.
|
|
*/
|
|
|
|
static void
|
|
CmdInvSuccessfulRequest(
|
|
char *message,
|
|
void *data2)
|
|
|
|
{
|
|
_DtActInvRecT *invp = NULL;
|
|
_DtActChildRecT *childrecp = NULL;
|
|
|
|
CallbackData *data = (CallbackData *) data2;
|
|
|
|
/*
|
|
* Mark this invocation step as done
|
|
* The child process itself may not be done.
|
|
*/
|
|
if ((invp = _DtActFindInvRec(data->requestPtr->invocId)) != NULL )
|
|
{
|
|
extern void *_DtCmdCheckQForId(DtActionInvocationID id);
|
|
|
|
SET_INV_DONE(invp->state);
|
|
RESET_INV_CMD_QUEUED(invp->state);
|
|
/*
|
|
* Are there still more commands queued for this request ?
|
|
*/
|
|
if ( _DtCmdCheckQForId(invp->id) )
|
|
{
|
|
/*
|
|
* If so; set the command queued bit
|
|
*/
|
|
SET_INV_CMD_QUEUED(invp->state);
|
|
}
|
|
/*
|
|
* RWV:
|
|
* This may not be the right place to set the child state for
|
|
* command actions. The child process may already have communicated
|
|
* its status via TT messaging OR it may already have exited.
|
|
* For now we set the state here -- till we find a better place.
|
|
*/
|
|
if (childrecp = _DtActFindChildRec(invp->id,data->requestPtr->childId))
|
|
childrecp->childState = _DtActCHILD_ALIVE_UNKNOWN;
|
|
else
|
|
myassert(0 /* could not find child record */ );
|
|
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
}
|
|
else
|
|
myassert( 0 /* Couldn't find an invocation record */);
|
|
|
|
_DtFreeRequest(data->requestPtr);
|
|
XtFree(data->actionLabel);
|
|
XtFree((char *)data);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* This callback is invoked when the Command Invoker library has failed
|
|
* to exectue an action. It there are additional execHosts to be processed,
|
|
* then try the command again, using the next host. If there are no more
|
|
* hosts, then post an error dialog, and give up (freeing all data
|
|
* associated with this request).
|
|
*/
|
|
|
|
static void
|
|
CmdInvFailedRequest(
|
|
char *error_message,
|
|
void *data2)
|
|
|
|
{
|
|
CallbackData * data = (CallbackData *) data2;
|
|
String msg = error_message;
|
|
ActionPtr action;
|
|
ActionRequest * request;
|
|
_DtActChildRecT *childp = NULL;
|
|
_DtActInvRecT *invp = NULL;
|
|
|
|
|
|
/*
|
|
* If this was not the last host in the execHost list, then retry
|
|
* the request, using the next host; if this was the last host,
|
|
* then we failed, and it is time to post an error dialog. If the
|
|
* host list had only one item, then to be backwards compatible,
|
|
* we will display the message returned by the command invoker.
|
|
* Otherwise, we will simple display the list of execHosts, along
|
|
* with a message saying they could not be accessed.
|
|
*/
|
|
request = data->requestPtr;
|
|
|
|
if (request->clonedAction)
|
|
action = request->clonedAction;
|
|
else
|
|
action = data->actionPtr;
|
|
request->hostIndex++;
|
|
AddFailedHostToList(request, request->currentHost);
|
|
|
|
if ( !(invp = _DtActFindInvRec(request->invocId) ) )
|
|
myassert( 0 /* could not find invocation record */ );
|
|
|
|
if ( !(childp=_DtActFindChildRec(request->invocId,request->childId)))
|
|
myassert( 0 /* could not find child record */ );
|
|
|
|
/*
|
|
* Make sure the CMD_QUEUED bit is set correctly
|
|
*/
|
|
if ( invp )
|
|
{
|
|
extern void *_DtCmdCheckQForId(DtActionInvocationID id);
|
|
|
|
SET_INV_DONE(invp->state);
|
|
RESET_INV_CMD_QUEUED(invp->state);
|
|
/*
|
|
* Are there still more commands queued for this request ?
|
|
*/
|
|
if ( _DtCmdCheckQForId(invp->id) )
|
|
{
|
|
/*
|
|
* If so; set the command queued bit
|
|
*/
|
|
SET_INV_CMD_QUEUED(invp->state);
|
|
}
|
|
}
|
|
|
|
if (request->hostIndex < action->u.cmd.execHostCount)
|
|
{
|
|
/*
|
|
* Free up the child structure for the failed command request
|
|
* We may be trying again on another host but a new child rec
|
|
* will be allocated in PrepareAndExecute().
|
|
*/
|
|
|
|
if ( invp && childp )
|
|
{
|
|
/*
|
|
* Delete child record for failed exec on this host
|
|
*/
|
|
_DtActDeleteChildRec(invp,childp);
|
|
SET_INV_PENDING(invp->state);
|
|
if ( ! invp->numChildren )
|
|
RESET_INV_WORKING(invp->state);
|
|
}
|
|
|
|
/* Retry, using the next host */
|
|
PrepareAndExecuteAction(data->associatedWidget, request);
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( invp && childp )
|
|
{
|
|
/*
|
|
* RWV ---
|
|
* How can we tell if the Invocation COMPLETE bit
|
|
* needs to be set here?
|
|
* How about if no invocation is pending or working?
|
|
*/
|
|
SET_INV_ERROR(invp->state);
|
|
childp->childState = _DtActCHILD_FAILED;
|
|
}
|
|
|
|
|
|
/* No more hosts (they all failed); put up error dialog */
|
|
if (action->u.cmd.execHostCount <= 1)
|
|
{
|
|
/* Be backwards compatible */
|
|
CommandInvokerError(data->associatedWidget,
|
|
action->label,
|
|
msg + data->offset);
|
|
}
|
|
else
|
|
{
|
|
MultiHostAccessError(data->associatedWidget, request->clonedAction->label,
|
|
request->badHostList);
|
|
}
|
|
|
|
|
|
/* Cleanup */
|
|
_DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
|
|
_DtFreeRequest(request);
|
|
XtFree(data->actionLabel);
|
|
}
|
|
XtFree((char *)data);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* This function maps a filename relative to 'host' to be relative to
|
|
* 'newHost'. If newHost is NULL, then the local host is assumed.
|
|
*
|
|
* The returned string must be freed by the caller.
|
|
*/
|
|
|
|
char *
|
|
_DtActMapFileName(
|
|
const char * curHost,
|
|
const char * dir,
|
|
const char * file,
|
|
const char * newHost )
|
|
{
|
|
char buf[MAXPATHLEN];
|
|
char *chp = NULL;
|
|
int clen = 0;
|
|
char *netpath = NULL;
|
|
char *path = NULL;
|
|
|
|
/*
|
|
* Create the full path name relative to curHost
|
|
*/
|
|
|
|
buf[0]='\0'; /* empty string to start with */
|
|
|
|
if ( dir )
|
|
strcpy(buf,dir);
|
|
if ( file )
|
|
{
|
|
/* check if there is already a '/' separator */
|
|
if ( *file != '/' )
|
|
{
|
|
DtLastChar(buf,&chp,&clen);
|
|
if ( !( (clen == 1) && (*chp == '/')) )
|
|
strcat(buf,"/");
|
|
}
|
|
strcat(buf,file);
|
|
}
|
|
|
|
/* We should have constructed a file name string now */
|
|
myassert(buf[0] != '\0');
|
|
|
|
if (newHost)
|
|
{
|
|
if ( _DtIsSameHost(curHost,newHost) )
|
|
{
|
|
/*
|
|
* The current host is the same as the new host
|
|
* so no file name translation is necessary
|
|
*/
|
|
return XtNewString(buf);
|
|
}
|
|
/*
|
|
* The current host is not the same as the new host -- find the
|
|
* cannonical netfile name then reinterpret it on the new host.
|
|
*/
|
|
switch ( tt_ptr_error(netpath = tt_host_file_netfile(curHost,buf)) )
|
|
{
|
|
case TT_OK:
|
|
break;
|
|
case TT_ERR_PATH:
|
|
netpath = NULL;
|
|
break;
|
|
case TT_ERR_DBAVAIL:
|
|
netpath = NULL;
|
|
break;
|
|
case TT_ERR_DBEXIST:
|
|
netpath = NULL;
|
|
break;
|
|
case TT_ERR_INTERNAL:
|
|
netpath = NULL;
|
|
break;
|
|
default:
|
|
netpath = NULL;
|
|
break;
|
|
}
|
|
if ( netpath )
|
|
{
|
|
switch ( tt_ptr_error(path = tt_host_netfile_file(newHost,netpath)) )
|
|
{
|
|
case TT_OK:
|
|
break;
|
|
case TT_ERR_PATH:
|
|
path = NULL;
|
|
break;
|
|
case TT_ERR_DBAVAIL:
|
|
path = NULL;
|
|
break;
|
|
case TT_ERR_DBEXIST:
|
|
path = NULL;
|
|
break;
|
|
case TT_ERR_INTERNAL:
|
|
path = NULL;
|
|
break;
|
|
default:
|
|
path = NULL;
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
/*
|
|
* Convert the file path which is relative to curHost to be
|
|
* relative to the local host.
|
|
*/
|
|
if ( _DtIsSameHost(curHost,NULL) )
|
|
{
|
|
/*
|
|
* The current host is the same as the local host
|
|
* so no file name translation is necessary
|
|
*/
|
|
return XtNewString(buf);
|
|
}
|
|
/*
|
|
* The current host is not the same as the local host -- find the
|
|
* cannonical netfile name then reinterpret it on the local host.
|
|
*/
|
|
switch ( tt_ptr_error(netpath = tt_host_file_netfile(curHost,buf)) )
|
|
{
|
|
case TT_OK:
|
|
break;
|
|
case TT_ERR_PATH:
|
|
netpath = NULL;
|
|
break;
|
|
case TT_ERR_DBAVAIL:
|
|
netpath = NULL;
|
|
break;
|
|
case TT_ERR_DBEXIST:
|
|
netpath = NULL;
|
|
break;
|
|
case TT_ERR_INTERNAL:
|
|
netpath = NULL;
|
|
break;
|
|
default:
|
|
netpath = NULL;
|
|
break;
|
|
}
|
|
if ( netpath )
|
|
{
|
|
switch ( tt_ptr_error(path = tt_netfile_file(netpath)) )
|
|
{
|
|
case TT_OK:
|
|
break;
|
|
case TT_ERR_PATH:
|
|
path = NULL;
|
|
break;
|
|
case TT_ERR_DBAVAIL:
|
|
path = NULL;
|
|
break;
|
|
case TT_ERR_DBEXIST:
|
|
path = NULL;
|
|
break;
|
|
case TT_ERR_INTERNAL:
|
|
path = NULL;
|
|
break;
|
|
default:
|
|
path = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free up the memory allocated by tooltalk filenaming code here so
|
|
* downstream code need not worry about it.
|
|
*/
|
|
if ( netpath )
|
|
tt_free(netpath);
|
|
if ( path )
|
|
{
|
|
char *s = path;
|
|
path = XtNewString(s);
|
|
tt_free(s);
|
|
}
|
|
|
|
return path;
|
|
}
|