2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /* $TOG: Action.c /main/28 1999/09/16 14:55:25 mgreess $ */
25 * (c) Copyright 1997, The Open Group
27 /*************************************<+>*************************************
28 *****************************************************************************
34 ** Description: This file contains the action library source code.
37 ** (c) Copyright 1993, 1994 Hewlett-Packard Company
38 ** (c) Copyright 1993, 1994 International Business Machines Corp.
39 ** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
40 ** (c) Copyright 1993, 1994 Novell, Inc.
43 ****************************************************************************
44 ************************************<+>*************************************/
49 #include <sys/types.h>
51 #include <sys/param.h>
53 #ifdef _SUN_OS /* Need this for the strtod () call */
54 #include <floatingpoint.h>
57 #define X_INCLUDE_STRING_H
58 #define XOS_USE_XT_LOCKING
59 #include <X11/Xos_r.h>
64 #include <X11/Intrinsic.h>
66 #include <Dt/CmdInv.h>
70 #include <Dt/Message.h>
71 #include <Dt/Connect.h>
72 #include <Dt/Indicator.h>
73 #include <Dt/DtNlUtils.h>
74 #include <Dt/CommandM.h>
75 #include <Dt/Utility.h>
76 #include <Dt/Service.h>
77 #include <Dt/UserMsg.h>
80 #include <Xm/BulletinB.h>
81 #include <Xm/DialogS.h>
84 #include <Xm/LabelG.h>
86 #include <Xm/SeparatoG.h>
87 #include <Xm/PushBG.h>
88 #include <Xm/MessageB.h>
89 #include <Xm/MwmUtil.h>
90 #include <Xm/Protocols.h>
92 #include <Dt/ActionP.h>
93 #include <Dt/ActionUtilP.h>
94 #include <Dt/ActionDb.h>
95 #include <Dt/ActionFind.h>
98 #include <Dt/Action.h>
100 #include "myassertP.h"
101 #include "DtSvcLock.h"
103 #ifndef CDE_INSTALLATION_TOP
104 #define CDE_INSTALLATION_TOP "/opt/dt"
107 extern char * _DtStripSpaces(
109 extern char * _DtDbPathIdToString( DtDbPathId pathId) ;
111 #define _MAX_MAP_ATTEMPTS 100 /* Maximum nuber of "MAPS" that will
113 #define _DT_ACTION_MAX_CLOSE_TRIES 5
115 /******** Public Function Declarations ********/
117 void _DtCreateErrorDialog(
121 Boolean _DtCompileMessagePiece(
123 ActionRequest *request,
128 unsigned long processingMask,
129 Boolean ** paramUsed,
130 int * promptDataIndex ) ;
131 ActionRequest * _DtCloneRequest (
132 ActionRequest * request) ;
134 ActionRequest *request) ;
136 char * _DtFindCwd( void ) ;
138 char * _DtActMapFileName(
139 const char * curHost,
142 const char * newHost ) ;
144 extern void _DtProcessTtRequest(
146 ActionRequest *request,
148 char * relPathDir ) ;
149 extern Tt_status _DtInitializeToolTalk(Widget w);
151 extern Boolean _DtEmptyString(
154 /******** End Public Function Declarations ********/
157 /******** Static Function Declarations ********/
160 static void FreeErrorDialog(
163 XtPointer call_data) ;
164 static void InvalidFilename(
168 static void HostAccessError(
172 static void MultiHostAccessError(
176 static void NoActionError(
178 DtShmBoson origNameQuark,
184 static void MapError(
186 char * actionName ) ;
187 static void CommandInvokerError(
190 char * errorString) ;
191 static void NoToolTalkConnectionError(
195 static void TmpFileCreateError(
199 static void TmpFileOpenError(
203 static void TmpFileWriteError(
207 static void UnSupportedObject(
211 static void SetExecHost(
212 ActionRequest * request) ;
213 static void ParseHostList (
215 char *** hostListPtr,
216 int * hostListSizePtr,
217 int * hostCountPtr) ;
218 static void RemoveDuplicateHostNames (
220 int * hostCountPtr ) ;
221 static void AddFailedHostToList (
222 ActionRequest * request,
224 static int _DtAddEntry(
228 static void TryToTypeFile(
233 char ** resolvedPath);
234 static ActionRequest * CreateActionRequest(
243 _DtActInvRecT *invp);
244 static _DtActInvRecT *CreateInvocationRecord(
249 static Boolean ParseFileArgument(
251 ActionRequest * request,
252 ObjectData * objectData,
257 static void AddPrompt(
261 PromptEntry **prompts) ;
262 static int MatchParamsToAction(
263 ActionRequest *request,
265 PromptEntry **prompts) ;
266 static void ProcessOneSegment(
267 ActionRequest * request,
269 PromptEntry **prompts,
271 Boolean * argsOptionFound,
272 int * lastArgReferenced,
274 Boolean * paramUsed) ;
275 static ActionPtr CloneActionDBEntry(
277 static void CloneParsedMessage(
278 parsedMsg * old_pmsg,
279 parsedMsg * new_pmsg ) ;
280 static void FreeParsedMessage(
281 parsedMsg * parsedMessage) ;
282 static parsedMsg * CloneParsedMessageArray(
283 parsedMsg * pmsgArray,
285 static void FreeParsedMessageArray(
286 parsedMsg * parsedMessageArray,
288 static Boolean InsertArgumentString(
292 ActionRequest *request,
297 Boolean addLeadingSpace,
298 unsigned long processingMask ) ;
299 static void InsertUnmappedArgumentString(
303 Boolean addLeadingSpace ) ;
304 static char * GrowMsgBuffer(
308 static void CmdInvSuccessfulRequest(
311 static void CmdInvFailedRequest(
314 static void InitiateDtRequest(
316 ActionRequest *request) ;
317 static Boolean ResolveDtNotifyMessagePieces(
319 ActionRequest *request,
321 char * relPathDir ) ;
322 static void InitiateDtNotifyMessage(
324 ActionRequest *request ) ;
325 static void PrepareAndExecuteAction(
327 ActionRequest *request);
328 static void __ExtractCWD(
329 ActionRequest *request,
332 Boolean useObjectInfo) ;
333 static void ContinueRequest(
336 XtPointer call_data) ;
337 static void CancelRequest(
340 XtPointer call_data) ;
341 static void CreateContinueDialog(
343 ActionRequest *request,
345 PromptEntry *prompts) ;
346 static void CancelPromptDialog(
348 PromptDialog *dialog,
349 XtPointer call_data) ;
350 static void ProcessPromptDialog(
352 PromptDialog *dialog,
353 XtPointer call_data) ;
354 static void ChangePromptTraversal(
356 PromptDialog *dialog,
357 XtPointer call_data) ;
358 static void CreatePromptDialog(
360 ActionRequest *request,
362 PromptEntry *prompts) ;
363 static Boolean MoreArgumentsToProcess(
364 ActionRequest *request) ;
365 static Boolean ProcessRequest(
367 ActionRequest *request) ;
368 static void InitLocalizedStrings( void ) ;
369 static int LinkToTypeQuark(
373 char **resolvedPath) ;
374 static void CancelOut(
378 XtPointer num_params);
379 static void InitiateCommandInvokerRequest(
381 ActionRequest *request,
384 static void ProcessCommandInvokerRequest(
386 ActionRequest *request,
389 static Boolean ResolveCommandInvokerMessagePieces(
391 ActionRequest *request,
394 static Tt_callback_action _DbReloadCB(Tt_message m, Tt_pattern p);
395 static void _DtActTimerCB( XtPointer clientData,
396 XtIntervalId timerId);
397 static void _DtActIndicatorCB( XtPointer clientData,
398 XtIntervalId timerId);
400 /******** End Static Function Declarations ********/
403 /* Pointers to localizable strings */
404 static String PromptDialogTitle;
405 static String ErrorPostfix;
406 static String PromptDialogLabel;
407 static String ContinueMessage;
408 static String HostErrorMsg;
409 static String HostErrorMsg2;
410 static String NoActionMsg;
411 static String NoActionMsg2;
412 static String NoActionMsg3;
413 static String MapErrorMsg;
414 static String InvalidFileMsg;
415 static String MultiHostErrorMsg;
416 static String IcccmReqErrorMsg;
417 static String NoToolTalkConnMsg;
418 static String UnSupportedObjMsg;
419 static String TmpFileCreateErrorMsg;
420 static String TmpFileOpenErrorMsg;
421 static String TmpFileWriteErrorMsg;
425 * These error messages are used in the ActionTt.c file
426 * but were declared static to this file. -- For the
427 * time being I made them global to get things to work.
429 String ToolTalkErrorMsg;
430 String ToolTalkErrorMsg2;
431 String TtFileArgMapErr;
435 * Variables needed to make the "Escape" key remove the prompt dialog.
437 static XtActionsRec actionTable [] = {
438 {"Escape", (XtActionProc) CancelOut},
440 static char translations_escape[] = "<Key>osfCancel:Escape()";
444 #define PROMPT_HELP "vg_act"
447 /* Maximum Indicator activation duration (in milliseconds) */
449 #define INDICATOR_TIME (120 * 1000)
450 #define MIN_INDICATOR_TIME (5 * 1000)
454 /******************************************************************************
455 ******************************************************************************
457 * Public API Functions
459 ******************************************************************************
460 *****************************************************************************/
462 /*******************************************************************************
463 * DtActionInvoke -- invoke an action
464 * Widget w; ( widget for UI needs)
465 * char *action; ( action name )
466 * int aac; ( action arg count )
467 * ActionArgp aap; ( action argument pointer )
468 * char *termOpts; ( (opt) terminal options)
469 * char *execHost; ( (opt) execution host )
470 * char *cwd; ( (opt) cwd for this action )
471 * int useIndicator; ( 1 ==> use indicator, 0 ==> not )
472 * DtActionCallbackProc statusUpdateCb; (user supplied fcn)
473 * XtPointer client_data (user supplied client data)
474 *****************************************************************************/
485 DtActionCallbackProc statusUpdateCb,
486 XtPointer client_data)
489 ActionRequest *request;
490 char *contextHost= NULL;/* dummy to replace old parameter */
491 _DtActInvRecT *invp; /* pointer to invocation record */
492 Tt_status status = TT_OK;
493 static Boolean initialized = False;
494 extern XtAppContext *_DtInitAppContextp;
495 _DtSvcWidgetToAppContext(w);
498 _DtSvcAppLock(*_DtInitAppContextp);
500 /* We can't handle gadgets; use the parent, if necessary */
510 InitLocalizedStrings();
513 * Make sure Tooltalk is initialized
515 status = _DtInitializeToolTalk(w);
518 NoToolTalkConnectionError(w, action, status);
519 _DtSvcProcessUnlock();
524 * Create the DtTmp directory, if necessary.
526 tmpDir = _DtGetDtTmpDir();
528 mode = (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH );
529 if ( mkdir(tmpDir,mode) )
532 * Tmp directory creation failure.
534 * Make one attempt to create the parent directory if the error
535 * was because of a missing path component -- It may be because
536 * the "$HOME/.dt" directory hasn't yet been created.
538 if ( errno == ENOENT )
540 char *parentDir = _DtDirname(tmpDir);
541 if (parentDir && 0 == mkdir(parentDir,mode))
543 if (parentDir) XtFree(parentDir);
548 _DtInitializeCommandInvoker( XtDisplay(w),
549 NULL, /* bms tool class -- ignored */
550 NULL, /* application class -- ignored */
551 (DtSvcMsgContext)NULL, /* reloadDBHandler -- none here */
552 (_DtInitAppContextp ?
553 *_DtInitAppContextp : XtWidgetToApplicationContext(w)));
556 _DtSvcProcessUnlock();
558 /* Start the activity indicator */
559 if ( useIndicator ) {
560 _DtSendActivityNotification(INDICATOR_TIME);
563 if ( (invp = CreateInvocationRecord(action,w,aap,aac)) == NULL)
565 myassert( 0 ); /* no request structure --should never happen */
566 /* give up -- cannot allocate record */
568 if ( useIndicator ) _DtSendActivityDoneNotification();
569 _DtSvcAppUnlock(*_DtInitAppContextp);
570 _DtSvcAppUnlock(app);
576 if ( useIndicator ) {
577 /* Start timer for minimum blink time */
578 XtAppAddTimeOut(XtWidgetToApplicationContext(w),
580 (XtTimerCallbackProc) _DtActIndicatorCB,
581 (XtPointer) invp->id );
585 * Add user callback info to the new invocation record.
587 invp->client_data = client_data;
588 invp->cb = statusUpdateCb;
590 /* Create and fill in the request structure */
591 if ( !IS_INV_FINISHED(invp->state) && (request = CreateActionRequest (
592 w,action,aap,aac,termOpts,execHost,contextHost,cwd,invp)) != NULL)
594 if (ProcessRequest(w, request))
596 /* all done invoking ? */
597 RESET_INV_PENDING(invp->state);
599 /* We should only get here if all requests have been honored */
600 SET_INV_COMPLETE(invp->state);
603 * Evaluate whether we are done with this invocation.
604 * We may have to return values to the caller.
606 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
607 _DtFreeRequest(request);
609 /* Otherwise, a dialog was posted; request will be freed later */
613 * Set the indicator that the invocation Id has been returned
614 * add a timer so that we can return status info once the
615 * caller has gotten the invocation Id.
617 SET_INV_ID_RETURNED(invp->state);
618 XtAppAddTimeOut(XtWidgetToApplicationContext(w),
619 0 /* call back immediately */,
620 (XtTimerCallbackProc) _DtActTimerCB,
621 (XtPointer) invp->id );
623 _DtSvcAppUnlock(*_DtInitAppContextp);
624 _DtSvcAppUnlock(app);
630 DtDbReloadNotify( DtDbReloadCallbackProc proc, XtPointer client_data)
635 extern XtAppContext *_DtInitAppContextp;
638 if (NULL == proc) return;
640 _DtSvcAppLock(*_DtInitAppContextp);
643 * Check if we need to initialize tooltalk
645 status = _DtInitializeToolTalk(NULL);
646 if (TT_OK != status) {
647 _DtSvcAppUnlock(*_DtInitAppContextp);
652 * This function register a ToolTalk pattern for every
655 pattern = tt_pattern_create();
656 if (tt_ptr_error(pattern) != TT_OK) {
657 _DtSvcAppUnlock(*_DtInitAppContextp);
661 if (tt_pattern_scope_add(pattern, TT_SESSION) != TT_OK) {
662 _DtSvcAppUnlock(*_DtInitAppContextp);
665 if (tt_pattern_category_set(pattern, TT_OBSERVE) != TT_OK) {
666 _DtSvcAppUnlock(*_DtInitAppContextp);
669 if (tt_pattern_class_add(pattern, TT_NOTICE) != TT_OK) {
670 _DtSvcAppUnlock(*_DtInitAppContextp);
673 if (tt_pattern_state_add(pattern, TT_SENT) != TT_OK) {
674 _DtSvcAppUnlock(*_DtInitAppContextp);
677 sessId = tt_default_session();
678 if (tt_pattern_session_add(pattern, sessId) != TT_OK) {
679 _DtSvcAppUnlock(*_DtInitAppContextp);
683 if (tt_pattern_op_add(pattern, "DtTypes_Reloaded") != TT_OK) {
684 _DtSvcAppUnlock(*_DtInitAppContextp);
689 * Store information needed by the callback in the user data
690 * fields of the pattern.
692 status = tt_pattern_user_set(pattern, 0, (void *)proc);
693 if (status != TT_OK) {
694 _DtSvcAppUnlock(*_DtInitAppContextp);
697 status = tt_pattern_user_set(pattern, 1, (void *)client_data);
698 if (status != TT_OK) {
699 _DtSvcAppUnlock(*_DtInitAppContextp);
704 * _DbReloadCB is the ToolTalk callback which will call
707 if (tt_pattern_callback_add(pattern, _DbReloadCB) != TT_OK) {
708 _DtSvcAppUnlock(*_DtInitAppContextp);
712 if (tt_pattern_register(pattern) != TT_OK) {
713 _DtSvcAppUnlock(*_DtInitAppContextp);
717 _DtSvcAppUnlock(*_DtInitAppContextp);
721 /******************************************************************************
722 ******************************************************************************
724 * Private API Functions
726 ******************************************************************************
727 *****************************************************************************/
730 _DtActTimerCB( XtPointer clientData, XtIntervalId IntId)
732 _DtActExecutionLeafNodeCleanup((unsigned long)clientData,
737 _DtActIndicatorCB( XtPointer clientData, XtIntervalId IntId )
739 unsigned long invocId = (unsigned long) clientData;
740 _DtActInvRecT *invRecP = _DtActFindInvRec( invocId );
742 if ( !invRecP || IS_INV_FINISHED(invRecP->state) )
744 /* Turn off the activity indicator */
745 _DtSendActivityDoneNotification();
750 * Let the action turn off the indicator when invocation
753 SET_INV_INDICATOR_ON(invRecP->state);
757 /***************************************************************************
759 * Routines and static data to support DtDbReloadNotify which supplies
760 * the user with transparent access to the messaging system for
761 * notification of action/datatypes database changes.
763 ****************************************************************************/
767 * A ToolTalk callback function used to map callback arguments to
768 * the callback function specified by the user. This function invokes the
769 * user-defined DtReloadNotifyProc callback with the desired client_data.
772 static Tt_callback_action
773 _DbReloadCB(Tt_message m, Tt_pattern p)
775 DtDbReloadCallbackProc proc;
776 XtPointer client_data;
779 * user data 0: DtDbReloadCallbackProc proc;
780 * user data 1: XtPointer client_data;
782 proc = (DtDbReloadCallbackProc)tt_pattern_user(p, 0);
783 client_data = (XtPointer)tt_pattern_user(p, 1);
786 * Call registered callback function.
788 if (proc) (*proc)(client_data);
790 return TT_CALLBACK_PROCESSED;
794 /***************************************************************************/
795 /***************************************************************************/
796 /* Error Dialog Code */
797 /***************************************************************************/
798 /***************************************************************************/
801 * 'Ok' callback for the generic error dialogs. It will simply destroy
809 XtPointer call_data )
812 XtDestroyWidget(XtParent(w));
817 * Generic function used to create an error dialog.
821 _DtCreateErrorDialog(
832 XWindowAttributes xwa;
834 Boolean is_mapped = False;
837 fmt = XtNewString((char *)Dt11GETMESSAGE(2, 1, "%1$s%2$s%3$s"));
839 /* Create the title string for the dialog */
840 title = (char *)XtMalloc((Cardinal)(strlen(PromptDialogTitle) +
842 strlen(ErrorPostfix) +
845 (void)sprintf(title, fmt, PromptDialogTitle, actionName, ErrorPostfix);
849 ok = XmStringCreateLocalized((String)_DtOkString);
853 status = XGetWindowAttributes (XtDisplay (w), XtWindow (w), &xwa);
854 if (status && (xwa.map_state == IsViewable))
858 /* Create the error dialog */
860 XtSetArg(args[n], XmNmessageString, msg); n++;
861 XtSetArg(args[n], XmNtitle, title); n++;
862 XtSetArg(args[n], XmNokLabelString, ok); n++;
863 XtSetArg(args[n], XmNuseAsyncGeometry, True); n++;
866 XtSetArg (args[n], XmNdefaultPosition, False);
869 dialog = XmCreateErrorDialog(w, "errorDialog", args, n);
873 /* Set up callbacks */
874 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
875 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
876 XtAddCallback(dialog, XmNokCallback, FreeErrorDialog,
880 * If the widget is not mapped, center this dialog.
884 Dimension dialogWd, dialogHt;
885 Widget dialogShell = XtParent(dialog);
887 XtSetArg(args[0], XmNmappedWhenManaged, False);
888 XtSetValues(dialogShell, args, 1);
890 XtManageChild(dialog);
891 XtRealizeWidget (dialogShell);
893 XtSetArg(args[0], XmNwidth, &dialogWd);
894 XtSetArg(args[1], XmNheight, &dialogHt);
895 XtGetValues(dialog, args, 2);
897 XtSetArg (args[0], XmNx,
898 (WidthOfScreen(XtScreen(dialog)) - dialogWd) / 2U);
899 XtSetArg (args[1], XmNy,
900 (HeightOfScreen(XtScreen(dialog)) - dialogHt) / 2U);
901 XtSetValues (dialog, args, 2);
903 XtSetArg(args[0], XmNmappedWhenManaged, True);
904 XtSetValues(dialogShell, args, 1);
907 /* Display the dialog */
908 XtManageChild(dialog);
913 * Error handler for when the user supplied a file name which cannot
914 * be accessed. Displays an error dialog. Most often, this is caused
915 * when a filename with an embedded space is received in the object list.
925 XmString pt1, pt2, msg;
927 /* Construct the error message */
928 pt1 = XmStringCreateLocalized(InvalidFileMsg);
929 pt2 = XmStringCreateLocalized(filename);
930 msg = XmStringConcat(pt1, pt2);
932 _DtCreateErrorDialog(w, actionName, msg);
940 * Error handler for when the user supplied a host name which cannot
941 * be accessed. Displays an error dialog.
951 XmString pt1, pt2, pt3, msg, msg2;
953 /* Construct the error message */
954 pt1 = XmStringCreateLocalized(HostErrorMsg);
955 pt2 = XmStringCreateLocalized(hostName);
956 pt3 = XmStringCreateLocalized(HostErrorMsg2);
958 msg = XmStringConcat(pt1, pt2);
959 msg2 = XmStringConcat(msg, pt3);
961 _DtCreateErrorDialog(w, actionName, msg2);
972 * Error handler for when the user supplied a collection of host names
973 * which cannot be accessed. Displays an error dialog.
977 MultiHostAccessError(
984 char * buf = XtMalloc(strlen(MultiHostErrorMsg) + strlen(hostList) + 10);
986 sprintf(buf, MultiHostErrorMsg, hostList);
987 msg = XmStringCreateLocalized(buf);
988 _DtCreateErrorDialog(w, actionName, msg);
994 /***************************************************************************
996 * MapError - this function creates an error message when an action
997 * cannot be executed because of too many "MAPs".
1001 * Widget w; - Widget needed for posting the error dialog.
1003 * String actionName; - The name of the action.
1007 ****************************************************************************/
1015 XmString msg = XmStringCreateLocalized(MapErrorMsg);
1016 _DtCreateErrorDialog(w, actionName, msg);
1022 * Error handler for when an action definition cannot be found to
1023 * match an object of a particular type. Displays an error dialog.
1024 * A different message is displayed when no objects is supplied.
1029 * Since we use tmp files for buffers and do not support
1030 * strings; we need not add special code for buffer and
1033 /* fdt: Will need to also handle a string or a buffer ... eventually */
1038 DtShmBoson origNameQuark,
1046 char *msgbuf = XtMalloc(2*MAXPATHLEN);
1050 /* Construct the error message */
1051 if ((host == NULL) && (dir == NULL) && (file == NULL) && (type == NULL) )
1053 (void)sprintf(msgbuf,NoActionMsg2,actionName);
1055 else if ( (type != NULL ) && (file == NULL) && (dir == NULL))
1058 * We are dealing with a buffer object for which an action couldn't
1061 (void)sprintf(msgbuf,NoActionMsg3,actionName,type);
1065 name = (char *)XtMalloc((Cardinal)((host ? strlen(host) : 0) +
1066 (dir ? strlen(dir) : 0) +
1067 (file ? strlen(file) : 0) + 10));
1070 /* Construct the file name */
1073 (void)strcat(name, host);
1074 (void)strcat(name, ":");
1079 (void)strcat(name, dir);
1080 if (strcmp(dir, "/") != 0)
1081 (void)strcat(name, "/");
1085 (void)strcat(name, file);
1087 (void)sprintf(msgbuf,NoActionMsg,actionName,name,type);
1090 msg = XmStringCreateLocalized(msgbuf);
1092 _DtCreateErrorDialog(w, actionName, msg);
1100 * Error handler for when the Command Invoker detects an error, and
1101 * send us a failure response to our action request.
1102 * Display an error dialog.
1106 CommandInvokerError(
1109 String errorString )
1114 msg = XmStringCreateLocalized(errorString);
1115 _DtCreateErrorDialog(w, actionName, msg);
1121 * If an action requires a ToolTalk connection, and we were unable to get
1122 * one, then we will fail and post an error dialog.
1126 NoToolTalkConnectionError(
1133 char *errmsg, *statmsg;
1135 if (TT_OK == status)
1138 statmsg = tt_status_message(status);
1139 errmsg = XtMalloc(strlen(NoToolTalkConnMsg) + strlen(statmsg) + 2);
1140 sprintf(errmsg, NoToolTalkConnMsg, statmsg);
1142 msg = XmStringCreateLocalized(errmsg);
1143 _DtCreateErrorDialog(w, actionName, msg);
1150 TmpFileCreateError( Widget w, char *actionName, char *dirName)
1153 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1155 sprintf(msgbuf,TmpFileCreateErrorMsg,_DtActNULL_GUARD(dirName),
1158 msg = XmStringCreateLocalized(msgbuf);
1159 _DtCreateErrorDialog(w, actionName, msg);
1165 TmpFileOpenError( Widget w, char *actionName, char *fileName)
1168 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1170 sprintf(msgbuf,TmpFileOpenErrorMsg,_DtActNULL_GUARD(fileName),
1173 msg = XmStringCreateLocalized(msgbuf);
1174 _DtCreateErrorDialog(w, actionName, msg);
1180 TmpFileWriteError( Widget w, char *actionName, char *fileName)
1183 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1185 sprintf(msgbuf,TmpFileWriteErrorMsg,_DtActNULL_GUARD(fileName),
1188 msg = XmStringCreateLocalized(msgbuf);
1189 _DtCreateErrorDialog(w, actionName, msg);
1195 UnSupportedObject( Widget w, char *actionName, int objClass)
1198 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1200 sprintf(msgbuf,UnSupportedObjMsg,objClass,actionName);
1202 msg = XmStringCreateLocalized(msgbuf);
1203 _DtCreateErrorDialog(w, actionName, msg);
1211 /***************************************************************************/
1212 /***************************************************************************/
1213 /* Main Work Functions For _DtActionInvoke() */
1214 /***************************************************************************/
1215 /***************************************************************************/
1219 * Load the globals pointing to any localizable strings.
1223 InitLocalizedStrings( void )
1226 PromptDialogTitle = XtNewString(((char *)Dt11GETMESSAGE(2, 3, "Action: ")));
1227 ErrorPostfix = XtNewString(((char *)Dt11GETMESSAGE(2, 4, " [Error]")));
1228 PromptDialogLabel = XtNewString(
1229 ((char *)Dt11GETMESSAGE(2, 5, "Please enter the following information:")));
1230 ContinueMessage = XtNewString(
1231 ((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.")));
1232 HostErrorMsg =XtNewString(((char *)Dt11GETMESSAGE(2, 7, "The following host was not accessible:\n\n ")));
1235 HostErrorMsg2 =XtNewString(((char *)Dt11GETMESSAGE(2, 8, "\n\nThis may be because the remote host's file\nsystem is not properly mounted.\n\n")));
1237 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")));
1239 NoActionMsg =XtNewString(
1240 ((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")));
1241 NoActionMsg2 = XtNewString(((char *)Dt11GETMESSAGE(2, 10, "Action \"%s\" was not found.\n")));
1242 InvalidFileMsg = XtNewString(((char *)Dt11GETMESSAGE(2, 11, "The following file was not found:\n\n ")));
1243 MapErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2, 12, "This action cannot be executed because it contains too\nmany levels of MAPs, or the mapping is \"circular\".")));
1244 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?")));
1245 IcccmReqErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2,14,"The request to service this action has failed")));
1246 NoToolTalkConnMsg = XtNewString(((char *)Dt11GETMESSAGE(2,15,"The request to service this action has failed.\nA ToolTalk connection could not be established:\n\n%s")));
1247 ToolTalkErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2,16, "The request to service this action has failed")));
1248 ToolTalkErrorMsg2 = XtNewString(((char *)Dt11GETMESSAGE(2,17, "The request to service this action has failed for the following reason:\n\n %s")));
1249 TtFileArgMapErr = XtNewString((char *)Dt11GETMESSAGE(2,18,"An error occurred while attempting to map one of\nthe file arguments."));
1250 NoActionMsg3 =XtNewString(
1251 ((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")));
1252 UnSupportedObjMsg = XtNewString(
1253 ((char *)Dt11GETMESSAGE(2, 21, "Unsupported input object class: \"%d\"\nfor action: \"%s\".")));
1254 TmpFileCreateErrorMsg = XtNewString(
1255 ((char *)Dt11GETMESSAGE(2, 22, "Unable to create a temporary file in directory: \"%s\"\nfor the action named: \"%s\"")));
1256 TmpFileOpenErrorMsg = XtNewString(
1257 ((char *)Dt11GETMESSAGE(2, 23, "Unable to open a temporary file: \"%s\"\nfor the action named: \"%s\"")));
1258 TmpFileWriteErrorMsg = XtNewString(
1259 ((char *)Dt11GETMESSAGE(2, 24, "Unable to write a temporary file: \"%s\"\nfor the action named: \"%s\"")));
1263 * This function takes the information supplied by the caller of
1264 * _DtActionInvoke(), and turns it into an internal format. This
1265 * includes parsing out each of the file names, and converting the
1266 * type from a string to an integer.
1268 * The structure returned must be freed up eventually.
1271 static ActionRequest *
1272 CreateActionRequest(
1281 _DtActInvRecT *invp )
1286 ObjectData * objectDataArray;
1287 ObjectData objectData;
1288 ActionRequest * request;
1291 /* Allocate a new request structure -- zero filled */
1292 request = (ActionRequest *) XtCalloc(1,(Cardinal)sizeof(ActionRequest));
1294 request->actionName = XtNewString(actionName);
1297 request->termOpts = XtNewString(termOpts);
1300 request->execHost = XtNewString(execHost);
1303 request->cwdHost = XtNewString(cwdHost);
1306 request->cwdDir = XtNewString(cwdDir);
1308 request->objsUsed = -1; /* -1 => not yet determined */
1311 request->invocId = invp->id;
1313 /* If there are no objects, then there's no reason to continue */
1314 if ((aac <= 0) || (aap == NULL))
1318 * Allocate space for all the object data at once
1320 objectDataArray = (ObjectData *) XtCalloc(aac,(sizeof(ObjectData)));
1323 * process object names -- assume all file names are of the form
1324 * /path/file (do NOT allow host:/path/file)
1326 for ( i = 0; i < aac ; i++ )
1328 memset((void *)&objectData,0,sizeof(ObjectData));
1329 if ( (aap+i)->argClass == DtACTION_FILE )
1331 if (ParseFileArgument(w, request, &objectData, NULL ,
1332 aap[i].u.file.name, NULL , True))
1334 XtFree((char *)objectDataArray);
1338 else if ( (aap+i)->argClass == DtACTION_BUFFER )
1341 * Check if we've already created a tmp file for this buffer
1342 * if so fill in the request structure as if this were a file
1345 if ( invp->info[i].name )
1348 * Use the tmp file name and type stored in the invocation rec.
1349 * The FILE bit will be set in the object mask -- we will also
1350 * set the BUFFER bit to indicate that this is a tmp file
1351 * representing a buffer.
1353 if (ParseFileArgument(w, request, &objectData, NULL ,
1354 invp->info[i].name, invp->info[i].type , True))
1356 XtFree((char *)objectDataArray);
1360 * Set the buffer object bit as well -- and check whether
1361 * this buffer is intended to be writable, if not reset the
1362 * writable bit set in ParseFileArgument().
1364 SET_BUFFER_OBJ(objectData.mask);
1365 if ( !(aap[i].u.buffer.writable) )
1366 RESET_WRITE_OBJ(objectData.mask);
1368 * Save the buffer type info if we have it
1370 if ( aap[i].u.buffer.type )
1371 objectData.type = _DtDtsMMStringToBoson(aap[i].u.buffer.type);
1374 * Save the original buffer pointer and size for this
1377 if ( aap[i].u.buffer.bp )
1379 objectData.u.file.bp = aap[i].u.buffer.bp;
1380 objectData.u.file.sizebp = aap[i].u.buffer.size;
1386 /* Do buffer stuff here */
1387 SET_BUFFER_OBJ(objectData.mask);
1388 if ( aap[i].u.buffer.writable )
1389 SET_WRITE_OBJ(objectData.mask);
1392 * If the buffer type has been passed in to us save its quark
1393 * in the object structure now. When/if the type is determined
1394 * later this object record should be filled in with the
1397 if ( aap[i].u.buffer.type )
1398 objectData.type = _DtDtsMMStringToBoson(aap[i].u.buffer.type);
1402 * We have already determined the buffer type when creating
1403 * the invocation record. So get the type string
1404 * from the invocation record.
1406 myassert(invp->info[i].type);
1407 if (invp->info[i].type)
1409 objectData.type = _DtDtsMMStringToBoson(invp->info[i].type);
1414 * Save buffer contents
1416 if ( aap[i].u.buffer.bp )
1418 objectData.u.buffer.size = aap[i].u.buffer.size;
1419 objectData.u.buffer.bp = aap[i].u.buffer.bp;
1422 myassert(0 /* null buffer pointer */ );
1423 objectData.u.buffer.bp = NULL;
1424 objectData.u.buffer.size = 0;
1429 /* structure assignment */
1430 objectDataArray[i] = objectData;
1434 request->numObjects = numObjects;
1435 request->objects = objectDataArray;
1441 /******************************************************************************
1443 * static _DtActInvRecT *
1444 * CreateInvocationRecord(actionName,w,aap,aac)
1445 * Create an invocation record and fill in argument information
1446 * return a pointer to the newly allocated invocation record.
1448 *****************************************************************************/
1449 static _DtActInvRecT *
1450 CreateInvocationRecord(
1457 _DtActInvRecT *invp; /* pointer to invocation record */
1461 * allocate invocation record and ID to return to caller
1463 invp = _DtActAllocInvRec();
1467 * RWV --> ideally we would need error message here
1468 * but if we are unable to allocate the record we
1469 * would in all likelyhood be unable to allocate the
1470 * error message as well. We will essentially assume
1471 * allocation does not fail as is done throughout the
1476 myassert(invp->id != 0);
1478 SET_INV_PENDING(invp->state);
1480 invp->numChildren = 0;
1481 invp->childRec = NULL;
1484 * Fill in argument information
1494 invp->info = (_DtActArgInfo *)XtCalloc(aac,sizeof(_DtActArgInfo));
1496 for ( i=0; i < aac; i++ )
1498 if ( aap[i].argClass == DtACTION_BUFFER )
1500 int fd; /* tmp file descriptor */
1501 char *format; /* name template (printf format) */
1502 char *is_executable; /* IS_EXECUTABLE attribute */
1504 int bytesToWrite, bytesWritten;
1507 SET_BUFFER_OBJ(invp->info[i].mask);
1508 if ( aap[i].u.buffer.writable )
1509 SET_WRITE_OBJ(invp->info[i].mask);
1511 /* save original buffer size */
1512 invp->info[i].size = aap[i].u.buffer.size;
1515 * Determine the type of the buffer object.
1516 * Typing based on the object name takes precedence
1517 * over the type "hint".
1519 if ( aap[i].u.buffer.name
1520 || (aap[i].u.buffer.type == NULL) )
1522 tmp = DtDtsBufferToDataType(
1523 aap[i].u.buffer.bp,aap[i].u.buffer.size,
1524 aap[i].u.buffer.name);
1526 * Malloc our own copy of the type string so we won't
1527 * have to worry about when to call DtDtsFreeDataType() later.
1529 invp->info[i].type = XtNewString(tmp);
1530 DtDtsFreeDataType(tmp);
1533 invp->info[i].type = XtNewString(aap[i].u.buffer.type);
1537 * Simply create tmp files for ALL buffers.
1539 * This allows us to work around problems related to client
1540 * programs making subsequent changes to or freeing the memory
1541 * associated with the buffer before we are through with it.
1543 * For actions of type CMD we need to have files anyway.
1545 * Be sure to create tmp files with
1546 * a suffix proper for the buffer type.
1549 /* first determine the permissions for the new tmp file */
1551 DtDtsDataTypeToAttributeValue(invp->info[i].type,
1552 _DtActIS_EXECUTABLE,NULL);
1554 * The tmp file should at LEAST be readable
1556 mode=( S_IRUSR | S_IRGRP | S_IROTH );
1557 if ( aap[i].u.buffer.writable )
1558 mode |= ( S_IWUSR | S_IWGRP | S_IWOTH );
1560 && DtDtsIsTrue(is_executable) )
1561 mode |= ( S_IXUSR | S_IXGRP | S_IXOTH );
1563 DtDtsFreeAttributeValue(is_executable);
1566 if ( aap[i].u.buffer.name )
1569 * Attempt to use the name supplied for the buffer.
1571 invp->info[i].name = _DtActGenerateTmpFile(NULL,
1572 aap[i].u.buffer.name,mode,&fd);
1574 if ( !invp->info[i].name )
1577 * Generate tmp file based on format supplied for the
1580 format = DtDtsDataTypeToAttributeValue(invp->info[i].type,
1581 _DtActNAME_TEMPLATE,NULL);
1583 invp->info[i].name = _DtActGenerateTmpFile(NULL,format,mode,&fd);
1584 DtDtsFreeAttributeValue(format);
1586 if ( !invp->info[i].name )
1589 * Unable to generate usable tmp file name.
1592 * Error message makes assertion message redundant.
1593 * myassert(invp->info[i].name);
1595 TmpFileCreateError(w,actionName,_DtGetDtTmpDir());
1597 RESET_INV_PENDING(invp->state);
1598 SET_INV_ERROR(invp->state);
1599 SET_INV_CANCEL(invp->state);
1606 * Write contents of buffer to temp file
1608 myassert( fd >= 0 );
1609 for ( bytesToWrite = aap[i].u.buffer.size, bytesWritten = 0;
1611 bytesToWrite -= bytesWritten)
1613 bytesWritten = write(fd,aap[i].u.buffer.bp,bytesToWrite);
1614 if ( bytesWritten < 0 )
1616 if (errno == EINTR )
1623 myassert(0 /* Unrecoverable Write Error */);
1624 TmpFileWriteError(w,actionName,invp->info[i].name);
1627 (void) unlink(invp->info[i].name);
1629 RESET_INV_PENDING(invp->state);
1630 SET_INV_ERROR(invp->state);
1631 SET_INV_CANCEL(invp->state);
1641 /* error closing fd */
1642 if ( closeAttempts > _DT_ACTION_MAX_CLOSE_TRIES )
1647 case EBADF: /* invalid fd */
1650 case EINTR: /* interrupted sys call */
1652 continue; /* try again */
1653 case ENOSPC: /* Not enough space on NFS-mounted dev */
1654 TmpFileWriteError(w,actionName,
1655 invp->info[i].name);
1657 unlink(invp->info[i].name);
1658 RESET_INV_PENDING(invp->state);
1659 SET_INV_ERROR(invp->state);
1660 SET_INV_CANCEL(invp->state);
1662 /* try another close */
1665 /* It should have worked this time */
1669 default: /* anything else */
1673 break; /* only try again for conditions with continue */
1678 * Now that we have created a tmp file for this buffer
1679 * object set the FILE_OBJ flag as well as the buffer flag.
1680 * Objects with both the BUFFER and FILE flags set will be
1681 * recognized as buffers which have been written to tmp files.
1684 SET_FILE_OBJ(invp->info[i].mask);
1687 else if ( aap[i].argClass == DtACTION_FILE )
1689 invp->info[i].name = XtNewString(aap[i].u.file.name);
1691 SET_FILE_OBJ(invp->info[i].mask);
1692 SET_WRITE_OBJ(invp->info[i].mask);
1696 myassert( 0 /* unsupported object */ );
1697 UnSupportedObject(w, actionName, aap[i].argClass);
1699 RESET_INV_PENDING(invp->state);
1700 SET_INV_ERROR(invp->state);
1701 SET_INV_CANCEL(invp->state);
1715 ActionRequest * request,
1716 ObjectData * objectData,
1728 char *resolvedPath=NULL;
1730 /********************************************************************
1731 WE NO LONGER ACCEPT host:/path FORMAT
1732 if (host = _DtHostString(filename))
1734 hostId = _DtAddEntry(host, &request->hostNames, &request->numHostNames);
1738 ********************************************************************/
1741 hostId = _DtAddEntry(hostname, &request->hostNames,
1742 &request->numHostNames);
1746 if ( request->cwdHost != NULL )
1748 hostId = _DtAddEntry(request->cwdHost, &request->hostNames,
1749 &request->numHostNames);
1753 /* if all else fails use local host */
1754 host = _DtGetLocalHostName();
1755 hostId = _DtAddEntry(host, &request->hostNames,
1756 &request->numHostNames);
1761 objectData->u.file.origFilename = XtNewString(filename);
1762 objectData->u.file.origHostname = XtNewString(hostname);
1763 objectData->u.file.hostIndex = hostId;
1764 objectData->u.file.baseFilename = _DtBasename(filename);
1765 objectData->type = -1;
1767 /* Hash the directory name */
1768 if ( (dirName = _DtDirname(filename)) == NULL )
1770 if ( request->cwdDir )
1771 dirName = XtNewString(request->cwdDir);
1774 /* Default to current directory */
1775 dirName = _DtFindCwd();
1778 else if ( dirName[0] != '/' )
1781 * We have been provided with a relative path name
1782 * interpret it relative to the context directory.
1786 if ( request->cwdDir )
1787 tmpName=XtNewString(request->cwdDir);
1789 tmpName=_DtFindCwd();
1791 tmpName=XtRealloc(tmpName,strlen(tmpName)+strlen(dirName) +2);
1792 (void)strcat(tmpName,"/");
1793 (void)strcat(tmpName,dirName);
1798 if ( objectData->u.file.baseFilename == NULL || dirName == NULL )
1800 /* Invalidly formed file name */
1801 InvalidFilename(w, request->clonedAction->label, filename);
1802 _DtFreeRequest (request);
1804 XtFree(objectData->u.file.origFilename);
1805 XtFree(objectData->u.file.origHostname);
1806 XtFree(objectData->u.file.baseFilename);
1810 objectData->u.file.dirIndex = _DtAddEntry(dirName, &request->dirNames,
1811 &request->numDirNames);
1812 SET_UNKNOWN_IF_DIR(objectData->mask);
1813 SET_FILE_OBJ(objectData->mask);
1815 * default file objects are treated as writable/returned objects.
1817 SET_WRITE_OBJ(objectData->mask);
1820 * If a type has been provided for this file -- use it.
1821 * otherwise -- look up the type.
1825 if ( filetype && *filetype )
1826 objectData->type = _DtDtsMMStringToBoson(filetype);
1829 TryToTypeFile(objectData,request->hostNames[hostId],
1830 dirName, objectData->u.file.baseFilename,
1836 if ( !stat(resolvedPath,&sbuf) )
1838 /* successful stat of file -- check permissions */
1839 if ( !( sbuf.st_mode&S_IWOTH
1840 || sbuf.st_mode&S_IWGRP
1841 || sbuf.st_mode&S_IWUSR) )
1843 RESET_WRITE_OBJ(objectData->mask);
1850 XtFree(resolvedPath);
1857 * Returns a string representing the current working directory
1858 * for this process. This string must be freed up by the caller.
1859 * NOTE:This function does not replace sym_links with real paths.
1860 * This may be useful on networks where nfs mounts and symbolic
1861 * consistently named symbolic links are used to give the
1862 * impression of a single large network file system.
1870 char buf[MAXPATHLEN + 1];
1872 if ((tmp = getcwd(buf, MAXPATHLEN)) == NULL)
1875 DtProgName, DtError, NULL,
1876 "getcwd(): unable to get current directory", NULL);
1879 return (XtNewString(tmp));
1884 * Generic function which checks to see if the specified string is
1885 * already entered in the passed-in array; if so, then it will return
1886 * the index of the existing entry within the array; if not, then it
1887 * will grow the array, add the string into it, and then return the
1900 /* See if the string is already in the array */
1901 for (i = 0; i < *sizePtr; i++)
1903 if (strcmp(string, (*arrayPtr)[i]) == 0)
1907 /* Add the string */
1910 (*arrayPtr) = (String *)XtRealloc((String)*arrayPtr,
1911 (Cardinal)(sizeof(String) * (*sizePtr)));
1912 (*arrayPtr)[i] = XtNewString(string);
1918 * This function will type the indicated file, only if it is the first
1919 * parameter file; this is to improve performance, since in many cases,
1920 * only the first argument is used to 'type' the action, and the others
1921 * never need to be 'typed'.
1930 char **resolvedPath )
1933 /* Follow the link when typing files */
1934 obj->type = LinkToTypeQuark(host, dir, file, resolvedPath);
1939 * Given a file, follow any links, and base the filetype off of the
1940 * final file, not the link we are passed.
1948 char **resolvedPath )
1952 char link_path[MAXPATHLEN + 1];
1953 char file_name[MAXPATHLEN + 1];
1963 /* Used to check for symbolic link loops */
1966 history = (char **)XtMalloc(sizeof(char *) * history_size);
1968 path = _DtActMapFileName(host, dir, file, NULL);
1974 strcpy(file_name, path);
1977 while ((link_len = readlink(file_name, link_path, MAXPATHLEN)) > 0)
1979 link_path[link_len] = '\0';
1981 /* Force the link to be an absolute path, if necessary */
1982 if (link_path[0] != '/')
1984 /* Relative paths are relative to the current directory */
1985 end = DtStrrchr(file_name, '/') + 1;
1987 strcat(file_name, link_path);
1990 strcpy(file_name, link_path);
1992 /* Check for a recursive loop; abort if found */
1993 for (i = 0; i < history_count; i++)
1995 if (strcmp(file_name, history[i]) == 0)
1997 /* Drop back to last non-recursive portion of the path */
1998 strcpy(file_name, history[history_count-1]);
1999 for (i = 0; i < history_count; i++)
2001 XtFree((char *)history);
2002 dtype = DtDtsFileToDataType(file_name);
2003 dquark = _DtDtsMMStringToBoson(dtype);
2004 DtDtsFreeDataType(dtype);
2005 *resolvedPath = XtNewString(file_name);
2010 /* Add to the history list */
2011 if (history_count >= history_size)
2013 history_size += 100;
2014 history = (char **)XtRealloc((char *)history,
2015 sizeof(char *) * history_size);
2017 history[history_count++] = XtNewString(file_name);
2020 /* Free up the history list */
2021 for (i = 0; i < history_count; i++)
2023 XtFree((char *)history);
2025 dtype = DtDtsFileToDataType(file_name);
2026 dquark = _DtDtsMMStringToBoson(dtype);
2027 DtDtsFreeDataType(dtype);
2028 *resolvedPath = XtNewString(file_name);
2034 * Given a request, find the action to which it maps, and see if enough
2035 * parameters were supplied to allow the action to be started. It's
2036 * possible we may need to bring up a dialog to collect more data, or
2037 * we may need to invoke multiple actions.
2039 * The first time an action request is processed, we will check the
2040 * parameter situation, and will prompt the user, if necessary. The
2041 * second time the action request is processed (typically when the
2042 * user closes the parameter collecting dialog), we will simply invoke
2043 * the action with whatever we have; the user will not be prompted a
2044 * second time for any missing parameters.
2046 * If the request is processed (True is returned), then it is up to the
2047 * caller to free up the request structure.
2053 ActionRequest *request )
2059 PromptEntry * prompts;
2060 DtShmBoson actionQuark;
2061 Tt_status status = TT_OK;
2063 /* See if this is the first pass for the request */
2064 if (request->clonedAction == NULL)
2067 /* Always start with the first host, when processing a request */
2068 request->hostIndex = 0;
2070 /* Find the action DB entry which we map to */
2071 actionQuark = _DtDtsMMStringToBoson(request->actionName);
2072 RESET_TOO_MANY_MAPS(request->mask);
2074 if (actionQuark == -1 || (action = _DtActionFindDBEntry(request, actionQuark)) == NULL)
2077 * No action label is available here for error dialogs
2079 if (IS_TOO_MANY_MAPS(request->mask))
2081 MapError (w, request->actionName);
2084 else if (request->numObjects > 0)
2086 if (IS_FILE_OBJ(request->objects[0].mask))
2088 NoActionError(w, actionQuark,
2089 request->actionName,
2090 (char *)_DtDtsMMBosonToString(request->objects[0].type),
2091 request->hostNames[request->objects[0].u.file.hostIndex],
2092 request->dirNames[request->objects[0].u.file.dirIndex],
2093 request->objects[0].u.file.baseFilename);
2095 else if ( IS_BUFFER_OBJ(request->objects[0].mask) )
2098 * RWV -- may have to modify this call to generate a
2099 * message more suitable for buffer objects.
2101 NoActionError(w, actionQuark,
2102 request->actionName,
2103 (char *)_DtDtsMMBosonToString(request->objects[0].type),
2106 "Memory Object" /* filename */);
2108 myassert(0 /* should never get here */ );
2110 /* fdt: add code for strings
2111 * else if (IS_STRING_OBJ(request->objects[0].mask))
2115 NoActionError(w, actionQuark, request->actionName,
2116 NULL, NULL, NULL, NULL);
2119 * If we are in the middle of reprocessing a single argument
2120 * action, then continue with the next parameter. Otherwise,
2121 * this error terminates the request, so return.
2123 if (IS_REPROCESSING(request->mask) && MoreArgumentsToProcess(request))
2124 return(ProcessRequest(w, request));
2127 * We were never able to start this action.
2130 _DtActInvRecT *invRecP = _DtActFindInvRec(request->invocId);
2131 if (invRecP) SET_INV_ERROR(invRecP->state);
2136 request->clonedAction = action;
2139 * If this is a ToolTalk message, then before proceeding any further,
2140 * make sure we can get connected to a ToolTalk session. If we can't,
2141 * then we need to bail out.
2143 if (IS_TT_MSG(action->mask) &&
2144 (status = _DtInitializeToolTalk(NULL)) != TT_OK)
2146 NoToolTalkConnectionError(w, request->clonedAction->label, status);
2148 _DtActInvRecT *invRecP = _DtActFindInvRec(request->invocId);
2149 if (invRecP) SET_INV_ERROR(invRecP->state);
2154 /* Determine how we are sitting with parameters */
2155 unused = MatchParamsToAction(request, &numPrompts, &prompts);
2156 request->objsUsed = request->numObjects - unused;
2157 myassert(request->objsUsed >= 0);
2160 * Do we need to create a prompt dialog?
2161 * NOTE: if the action requires the user to be prompted, but the
2162 * user has supplied extra parameters, so he will be asked
2163 * to abort or continue, do the abort/continue dialog BEFORE
2164 * the prompt dialog; there's little sense in collecting
2165 * additional input if the user is going to abort the action!
2167 if ((prompts != NULL) &&
2168 ((unused == 0) || IS_ARG_SINGLE_ARG(action->mask) ||
2169 IS_ARG_NONE_FOUND(action->mask)))
2171 CreatePromptDialog(w, request, numPrompts, prompts);
2172 XtFree((char *)prompts);
2176 /* Were too many parameters supplied? */
2177 else if (unused > 0)
2180 * If the action only needs a single parameter, then we need
2181 * to fire off multiple instances of the action; otherwise,
2182 * prompt the user to continue or abort. An action requiring
2183 * no parameters is also treated like a single parameter action.
2185 if (IS_ARG_SINGLE_ARG(action->mask) || IS_ARG_NONE_FOUND(action->mask))
2188 PrepareAndExecuteAction(w, request);
2190 /* See if there are still more parameters to be processed */
2191 if (MoreArgumentsToProcess(request))
2192 return(ProcessRequest(w, request));
2197 * Postpone any further processing until the user either
2198 * tells us to continue, or abort.
2200 CreateContinueDialog(w, request, numPrompts, prompts);
2201 XtFree((char *)prompts);
2207 PrepareAndExecuteAction(w, request);
2213 PrepareAndExecuteAction(w, request);
2214 action = request->clonedAction;
2217 * If this is a single argument action, and we have more parameters
2218 * waiting to be processed, then continue processing them.
2220 if ((IS_ARG_SINGLE_ARG(action->mask) || IS_ARG_NONE_FOUND(action->mask))
2221 && (MoreArgumentsToProcess(request)))
2223 return(ProcessRequest(w, request));
2232 * This function is called at the point where we have collected all of the
2233 * information needed to actually initiate the action. We will use the
2234 * set of arguments passed into _DtActionInvoke(), along with any values
2235 * supplied through the prompt dialog. It is also at this point that
2236 * the thread of control will split, dependent upon the type of action
2237 * being executed (Command Invoker, Tooltalk).
2240 PrepareAndExecuteAction(
2242 ActionRequest *request )
2248 ActionPtr action = request->clonedAction;
2250 _DtActInvRecT *invp; /* pointer to invocation record */
2251 _DtActChildRecT *childrecp; /* pointer to child record */
2254 * We have gathered all the information necessary to invoke
2255 * this action all dialogs have been posted and processed.
2256 * Now create the action invocation record -- unless we are
2257 * in the midst of reprocessing an already invoked action.
2259 invp = _DtActFindInvRec(request->invocId);
2261 SET_INV_WORKING(invp->state);
2264 * Allocate a child rec -- fill it in
2266 if ( (childrecp = _DtActAllocChildRec(invp)) != NULL )
2268 request->childId = childrecp->childId;
2270 childrecp->childState = _DtActCHILD_PENDING_START;
2271 childrecp->mask = action->mask;
2273 childrecp->numObjects = request->objsUsed;
2276 myassert( 0 /* Unable to allocate childRec */ );
2280 * Before proceeding, we need to determine what host and directory
2281 * will be used when resolving relative pathnames.
2283 __ExtractCWD(request, &relPathHost, &relPathDir, False);
2284 if (IS_CMD(action->mask))
2287 * All buffer objects must be placed into temporary files for
2288 * command actions. This has already been done when the
2289 * request structure was created.
2291 if (childrecp && childrecp->numObjects > 0)
2294 (_DtActArgMap *)XtCalloc(childrecp->numObjects,
2295 sizeof(_DtActArgMap));
2297 for ( i = 0; i < childrecp->numObjects && i < invp->ac; i++ )
2299 childrecp->argMap[i].argN = i+1; /* ignored for CMD actions */
2300 childrecp->argMap[i].argIdx =
2301 i + request->objOffset; /* idx into invp->info[] */
2304 ProcessCommandInvokerRequest(w, request, relPathHost, relPathDir);
2306 else if (IS_TT_MSG(action->mask))
2311 * create argmap for returnable arguments --
2312 * i.e. those appearing in TT_ARGn_VALUE fields.
2314 * The requirement is that one and only one action argument may
2315 * appear in a TT_ARGn_VALUE field.
2317 * argMap is a sparse array which maps TT_ARGn_VALUEs to input
2318 * parameters. If a TT_ARGn_VALUE does not have an input parameter
2319 * as a value then the sentinel value "-1" is provided as the index.
2320 * Allocate enough space for all the TT_ARGn_VALUEs plus one for
2323 * The elements of the argMap array then represent:
2324 * argMap[ TT_ARG0, TT_ARG1, ...,TT_ARGN, TT_FILE]
2328 (_DtActArgMap *)XtCalloc( action->u.tt_msg.value_count + 1,
2329 sizeof(_DtActArgMap));
2331 for ( i = 0; i < action->u.tt_msg.value_count; i++)
2334 * Set index value to "-1". This value will indicate
2335 * TT_ARGn_VALUES which are NOT associated with input
2336 * parameters (action arguments). If there is an action
2337 * argument associated with this TT_ARGn_VALUE we will set
2340 childrecp->argMap[i].argIdx = -1;
2341 childrecp->argMap[i].argN = i;
2343 /* null argn value is valid -- so check MsgParts*/
2344 if (!action->u.tt_msg.tt_argn_value[i].numMsgParts)
2348 u.tt_msg.tt_argn_value[i].parsedMessage[0].keyword
2353 * TT_ARGn_VALUE fields should have only one arg keyword.
2355 myassert(action->u.tt_msg.tt_argn_value[i].numMsgParts == 1);
2357 u.tt_msg.tt_argn_value[i].parsedMessage[0].argNum;
2359 if ( ( argNum > 0 ) && ( argNum <= invp->ac ) )
2361 /* The ith message part must be returned */
2362 childrecp->argMap[i].argIdx =
2363 argNum + request->objOffset - 1;
2364 myassert( childrecp->argMap[i].argIdx >= 0 );
2368 * Add an argMap entry for the value of the TT_FILE field.
2369 * Tooltalk (e.g. media messages) sometimes uses this field
2370 * to pass values such as file names to the message receipient.
2371 * If the TT_FILE field has a single ARG keyword
2372 * then record that parameter number otherwise record "-1" as
2373 * was done for the value arguments above.
2375 childrecp->argMap[i].argIdx = -1;
2376 childrecp->argMap[i].argN = -1; /* Use "-1" as TT_FILE entry idx */
2377 if (action->u.tt_msg.tt_file.numMsgParts
2378 && action->u.tt_msg.tt_file.parsedMessage[0].keyword == ARG )
2381 u.tt_msg.tt_file.parsedMessage[0].argNum;
2382 if ( ( argNum > 0 ) && ( argNum <= invp->ac ) )
2384 /* The ith message part should be the last argMap entry */
2385 childrecp->argMap[i].argIdx =
2386 argNum + request->objOffset - 1;
2387 myassert( childrecp->argMap[i].argIdx >= 0 );
2392 _DtProcessTtRequest(w, request, relPathHost, relPathDir);
2396 * For now we are through invoking this child.
2397 * There may still be more children to invoke or we may have to
2398 * re-invoke this child (e.g. multi-host processing for commands).
2400 SET_INV_DONE(invp->state);
2402 /* Free up the path information */
2403 XtFree(relPathHost);
2409 * Determine the CWD to use; this information can be used to both
2410 * resolve relative filepaths, and to set the CWD used when executing
2411 * a command invoker request. When resolving relative paths, the
2412 * information specified for the first argument is not used (see case 2
2413 * below). It is determined using the following algorithm:
2415 * 1) Use the CWD specified in the action (if a cmd invoker action).
2416 * 2) If told to use the objects, then use the directory where the
2417 * object lives (if a regular file), or the object itself (if
2419 * 3) Use the CWD passed into _) by the application.
2420 * 4) Use the physical CWD of the application.
2422 * Both the host and directory paths must by freed by the caller.
2426 ActionRequest *request,
2429 Boolean useObjectInfo )
2435 ActionPtr action = request->clonedAction;
2437 /* Only dropped objects will have been 'typed' at this point */
2438 if (useObjectInfo && (IS_CMD(action->mask)) && (request->numObjects > 0) &&
2439 (request->objects[0].type >= 0) &&
2440 IS_FILE_OBJ(request->objects[0].mask))
2442 if (action->u.cmd.contextHost != NULL)
2443 *hostPtr = XtNewString(action->u.cmd.contextHost);
2446 *hostPtr = XtNewString(
2447 request->hostNames[request->objects[0].u.file.hostIndex]);
2450 if (IS_UNKNOWN_IF_DIR(request->objects[0].mask))
2453 char *theHost, *theDir;
2454 struct stat statInfo;
2456 RESET_UNKNOWN_IF_DIR(request->objects[0].mask);
2459 * The file may not have been checked yet, if it was never
2460 * referenced in the execution string; so .. we'll check
2463 theHost = request->hostNames[request->objects[0].u.file.hostIndex];
2464 theDir = request->dirNames[request->objects[0].u.file.dirIndex];
2465 nfsPath = _DtActMapFileName(theHost, theDir,
2466 request->objects[0].u.file.baseFilename, NULL);
2468 if (nfsPath && (stat(nfsPath, &statInfo) == 0) &&
2469 ((statInfo.st_mode & S_IFMT) == S_IFDIR))
2471 SET_DIR_OBJ(request->objects[0].mask);
2476 if (IS_DIR_OBJ(request->objects[0].mask))
2478 if (action->u.cmd.contextDir != NULL)
2479 *dirPtr = XtNewString(action->u.cmd.contextDir);
2482 *dirPtr = XtMalloc((Cardinal)
2483 (strlen(request->dirNames[request->objects[0].u.file.dirIndex]) +
2484 strlen(request->objects[0].u.file.baseFilename) + 2));
2486 request->dirNames[request->objects[0].u.file.dirIndex]);
2488 DtLastChar(*dirPtr, &lastCh, &lastChLen);
2489 if ((lastChLen != 1) || (*lastCh != '/'))
2490 (void)strcat(*dirPtr, "/");
2492 (void)strcat(*dirPtr, request->objects[0].u.file.baseFilename);
2497 if (action->u.cmd.contextDir != NULL)
2498 *dirPtr = XtNewString(action->u.cmd.contextDir);
2501 *dirPtr = XtNewString(
2502 request->dirNames[request->objects[0].u.file.dirIndex]);
2508 /* Use specified context, or get process context, if necessary */
2509 if (IS_CMD(action->mask) && (action->u.cmd.contextHost != NULL))
2510 *hostPtr = XtNewString(action->u.cmd.contextHost);
2511 else if (request->cwdHost)
2512 *hostPtr = XtNewString(request->cwdHost);
2515 *hostPtr = _DtGetLocalHostName();
2518 if (IS_CMD(action->mask) && (action->u.cmd.contextDir != NULL))
2519 *dirPtr = XtNewString(action->u.cmd.contextDir);
2520 else if (request->cwdDir)
2521 *dirPtr = XtNewString(request->cwdDir);
2523 *dirPtr = _DtFindCwd();
2529 * This function is used to prepare for the continued processing of
2530 * the parameters, when the action is a single argument action. It
2531 * free up any data which was associated with the previous parameter,
2532 * and cascades up any remaining parameters in the object array.
2536 MoreArgumentsToProcess(
2537 ActionRequest *request )
2544 if (request->numObjects <= 1)
2550 /* Repeat processing for the next argument */
2552 /* Cascade up the remaining, unprocess parameters */
2553 if (IS_FILE_OBJ(request->objects[0].mask))
2555 XtFree(request->objects[0].u.file.origFilename);
2556 XtFree(request->objects[0].u.file.origHostname);
2557 XtFree(request->objects[0].u.file.baseFilename);
2561 * Since we use tmp files for buffers and do not support
2562 * strings; we need not add special code for buffer and
2565 /* fdt: Add support for strings and buffers here
2566 * else if (IS_BUFFER_OBJ(request->objects[0].mask))
2567 * XtFree(request->objects[0].u.buffer.buffer);
2568 * else if (IS_STRING_OBJ(request->objects[0].mask))
2569 * XtFree(request->objects[0].u.string.string);
2571 for (i = 0; i < (request->numObjects - 1); i++)
2573 request->objects[i] = request->objects[i+1];
2575 request->numObjects--;
2576 request->objOffset++;
2577 request->objsUsed = 0;
2578 request->childId = 0;
2580 /* Free up our previously cloned action */
2581 _DtFreeActionStruct(request->clonedAction);
2582 request->clonedAction = NULL;
2584 /* Free up any leftover prompt strings */
2585 for (i = 0; i < request->numPromptInputs; i++)
2586 XtFree(request->promptInputs[i]);
2587 XtFree((char *)request->promptInputs);
2588 request->promptInputs = NULL;
2589 request->numPromptInputs = 0;
2590 SET_REPROCESSING(request->mask);
2591 XtFree(request->badHostList);
2592 request->badHostList = NULL;
2593 XtFree(request->currentHost);
2594 request->currentHost = NULL;
2595 request->hostIndex = 0;
2597 /* Type the object, if possible */
2598 if (IS_FILE_OBJ(request->objects[0].mask))
2600 if ((request->objects[0].u.file.hostIndex >= 0) &&
2601 (request->objects[0].u.file.dirIndex >= 0))
2603 path = _DtActMapFileName(
2604 request->hostNames[request->objects[0].u.file.hostIndex],
2605 request->dirNames[request->objects[0].u.file.dirIndex],
2606 request->objects[0].u.file.baseFilename, NULL);
2607 dtype = DtDtsFileToDataType(path);
2608 request->objects[0].type = _DtDtsMMStringToBoson(dtype);
2609 DtDtsFreeDataType(dtype);
2615 * Since we use tmp files for buffers and do not support
2616 * strings; we need not add special code for buffer and
2619 /* fdt: add support for buffers and strings here
2620 * else if (IS_BUFFER_OBJ(request->objects[0].mask))
2623 * else if (IS_STRING_OBJ(request->objects[0].mask))
2633 /***************************************************************************/
2634 /***************************************************************************/
2635 /* Functions For Cloning And Free Structures */
2636 /***************************************************************************/
2637 /***************************************************************************/
2641 * At the point that a request is sent, we need to save a copy of the
2642 * request, for future reference. We need to clone the request, since
2643 * the original request structure may get modified between the time we
2644 * send the message, and the time we need to reference it in the future.
2649 ActionRequest * request)
2652 ActionRequest * newRequest;
2655 newRequest = (ActionRequest *)XtMalloc((Cardinal)sizeof(ActionRequest));
2658 * Structure assignment to clone all scalar values
2659 * If a value is not explicitly set then it defaults to the same value
2660 * as in the original request. Pointers to malloc-ed memory should all
2661 * be replaced with pointers to a copy of the region.
2663 (*newRequest) = (*request);
2665 newRequest->actionName = XtNewString(request->actionName);
2667 if (request->numObjects > 0) {
2668 newRequest->objects = (ObjectData *)XtMalloc(sizeof(ObjectData) *
2669 request->numObjects);
2671 for (i = 0; i < request->numObjects; i++)
2673 newRequest->objects[i] = request->objects[i];
2674 if (IS_FILE_OBJ(request->objects[i].mask))
2676 newRequest->objects[i].u.file.origFilename =
2677 XtNewString(request->objects[i].u.file.origFilename);
2678 newRequest->objects[i].u.file.origHostname =
2679 XtNewString(request->objects[i].u.file.origHostname);
2680 newRequest->objects[i].u.file.baseFilename =
2681 XtNewString(request->objects[i].u.file.baseFilename);
2683 else if ( IS_BUFFER_OBJ(request->objects[i].mask) )
2687 * Since we are creating tmp files for all buffers
2688 * we should never have to copy a buffer's contents.
2690 * We should never reach this code because the FILE_OBJ
2691 * bit is set when we create tmp files for buffers.
2696 * Can we get by without copying buffer object contents?
2697 * if so -- how do we avoid freeing it twice OR
2698 * not freeing it at all?
2700 /* make a copy of the buffer */
2701 if ( request->objects[i].u.buffer.bp )
2703 myassert(newRequest->objects[i].u.buffer.size == request->objects[i].u.buffer.size);
2704 newRequest->objects[i].u.buffer.bp =
2705 XtMalloc( request->objects[i].u.buffer.size );
2706 memcpy(newRequest->objects[i].u.buffer.bp,
2707 request->objects[i].u.buffer.bp,
2708 newRequest->objects[i].u.buffer.size);
2712 myassert(0 /* no other object types supported */ );
2716 newRequest->numPromptInputs = request->numPromptInputs;
2717 if (request->numPromptInputs > 0) {
2718 newRequest->promptInputs = (char **)XtMalloc(sizeof(char *) *
2719 request->numPromptInputs);
2720 for (i = 0; i < request->numPromptInputs; i++)
2721 newRequest->promptInputs[i] = XtNewString(request->promptInputs[i]);
2724 newRequest->numHostNames = request->numHostNames;
2725 if (request->numHostNames > 0) {
2726 newRequest->hostNames = (char **)XtMalloc(sizeof(char *) *
2727 request->numHostNames);
2728 for (i = 0; i < request->numHostNames; i++)
2729 newRequest->hostNames[i] = XtNewString(request->hostNames[i]);
2732 newRequest->numDirNames = request->numDirNames;
2733 if (request->numDirNames > 0) {
2734 newRequest->dirNames = (char **)XtMalloc(sizeof(char *) *
2735 request->numDirNames);
2736 for (i = 0; i < request->numDirNames; i++)
2737 newRequest->dirNames[i] = XtNewString(request->dirNames[i]);
2740 newRequest->termOpts = XtNewString(request->termOpts);
2741 newRequest->cwdHost = XtNewString(request->cwdHost);
2742 newRequest->cwdDir = XtNewString(request->cwdDir);
2744 if (request->clonedAction)
2745 newRequest->clonedAction = CloneActionDBEntry(request->clonedAction);
2747 newRequest->clonedAction = NULL;
2749 newRequest->badHostList = XtNewString(request->badHostList);
2750 newRequest->currentHost = XtNewString(request->currentHost);
2751 newRequest->execHost = XtNewString(request->execHost);
2759 * Free up the contents of a request structure
2764 ActionRequest *request )
2769 XtFree(request->actionName);
2771 for (i = 0; i < request->numObjects; i++)
2773 if (IS_FILE_OBJ(request->objects[i].mask))
2775 XtFree(request->objects[i].u.file.origFilename);
2776 XtFree(request->objects[i].u.file.origHostname);
2777 XtFree(request->objects[i].u.file.baseFilename);
2781 * Since we use tmp files for buffers and do not support
2782 * strings; we need not add special code for buffer and
2785 /* fdt: Add support for buffers and strings here
2786 * else if (IS_BUFFER_OBJ(request->objects[i].mask)
2787 * XtFree(request->objects[i].u.buffer.buffer);
2788 * else if (IS_STRING_OBJ(request->objects[i].mask)
2789 * XtFree(request->objects[i].u.string.string);
2794 * Since the objectDataArray was malloced at once
2795 * we can free it at once.
2797 if (request->objects) XtFree((char *)request->objects);
2799 for (i = 0; i < request->numPromptInputs; i++)
2800 XtFree(request->promptInputs[i]);
2801 if (request->promptInputs) XtFree((char *)request->promptInputs);
2803 for (i = 0; i < request->numHostNames; i++)
2804 XtFree(request->hostNames[i]);
2805 if (request->hostNames) XtFree((char *)request->hostNames);
2807 for (i = 0; i < request->numDirNames; i++)
2808 XtFree(request->dirNames[i]);
2809 if (request->dirNames) XtFree((char *)request->dirNames);
2811 XtFree(request->termOpts);
2812 XtFree(request->cwdHost);
2813 XtFree(request->cwdDir);
2814 _DtFreeActionStruct(request->clonedAction);
2815 XtFree(request->badHostList);
2816 XtFree(request->currentHost);
2817 XtFree(request->execHost);
2819 XtFree ((char *)request);
2824 * Create a clone of an action DB entry
2832 ActionPtr newAction = (ActionPtr)XtMalloc((Cardinal)sizeof(Action));
2835 /* Clone each field */
2836 newAction->action = action->action;
2837 newAction->file_name_id = action->file_name_id;
2838 newAction->label = XtNewString(action->label);
2839 newAction->description = XtNewString(action->description);
2841 newAction->type_count = action->type_count;
2842 if (action->type_count > 0) {
2843 newAction->arg_types = (DtShmBoson *)XtMalloc(sizeof(DtShmBoson) *
2844 newAction->type_count);
2845 for (i = 0; i < newAction->type_count; i++)
2846 newAction->arg_types[i] = action->arg_types[i];
2849 newAction->arg_types = NULL;
2852 newAction->arg_count = action->arg_count;
2853 newAction->mask = action->mask;
2855 if (IS_CMD(action->mask))
2857 cmdAttr * newCmd = &(newAction->u.cmd);
2858 cmdAttr * oldCmd = &(action->u.cmd);
2860 CloneParsedMessage(&(oldCmd->execString), &(newCmd->execString));
2861 CloneParsedMessage(&(oldCmd->termOpts), &(newCmd->termOpts));
2862 newCmd->contextDir = XtNewString(oldCmd->contextDir);
2863 newCmd->contextHost = XtNewString(oldCmd->contextHost);
2864 CloneParsedMessage(&(oldCmd->execHosts), &(newCmd->execHosts));
2865 newCmd->execHostCount = oldCmd->execHostCount;
2866 if (oldCmd->execHostCount > 0) {
2867 newCmd->execHostArray = (char **)XtMalloc(sizeof(char *) *
2868 newCmd->execHostCount);
2869 for (i = 0; i < newCmd->execHostCount; i++)
2870 newCmd->execHostArray[i] = XtNewString(oldCmd->execHostArray[i]);
2873 newCmd->execHostArray = NULL;
2876 else if (IS_MAP(action->mask))
2878 newAction->u.map.map_action = action->u.map.map_action;
2880 else if (IS_TT_MSG(action->mask))
2882 tt_msgAttr * newMsg = &(newAction->u.tt_msg);
2883 tt_msgAttr * oldMsg = &(action->u.tt_msg);
2885 newMsg->tt_class = oldMsg->tt_class;
2886 newMsg->tt_scope = oldMsg->tt_scope;
2887 CloneParsedMessage(&(oldMsg->tt_op), &(newMsg->tt_op));
2888 CloneParsedMessage(&(oldMsg->tt_file), &(newMsg->tt_file));
2890 newMsg->mode_count = oldMsg->mode_count;
2891 if (oldMsg->mode_count > 0) {
2892 newMsg->tt_argn_mode =
2893 (int *)XtMalloc(sizeof(int) * newMsg->mode_count);
2894 for (i = 0; i < newMsg->mode_count; i++)
2895 newMsg->tt_argn_mode[i] = oldMsg->tt_argn_mode[i];
2898 newMsg->tt_argn_mode = NULL;
2901 newMsg->vtype_count = oldMsg->vtype_count;
2902 newMsg->tt_argn_vtype = CloneParsedMessageArray(oldMsg->tt_argn_vtype,
2903 oldMsg->vtype_count);
2905 newMsg->value_count = oldMsg->value_count;
2906 newMsg->tt_argn_value = CloneParsedMessageArray(oldMsg->tt_argn_value,
2907 oldMsg->value_count);
2909 newMsg->rep_type_count = oldMsg->rep_type_count;
2910 if (oldMsg->rep_type_count > 0) {
2911 newMsg->tt_argn_rep_type = (int *)XtMalloc(sizeof(int) *
2912 newMsg->rep_type_count);
2913 for (i = 0; i < newMsg->rep_type_count; i++)
2914 newMsg->tt_argn_rep_type[i] = oldMsg->tt_argn_rep_type[i];
2917 newMsg->tt_argn_rep_type = NULL;
2926 * Free up the contents of a request structure
2930 _DtFreeActionStruct(
2939 XtFree(action->label);
2940 XtFree(action->description);
2941 if (action->arg_types) XtFree((char *)action->arg_types);
2943 if (IS_CMD(action->mask))
2945 FreeParsedMessage(&(action->u.cmd.execString));
2946 FreeParsedMessage(&(action->u.cmd.termOpts));
2947 XtFree(action->u.cmd.contextDir);
2948 XtFree(action->u.cmd.contextHost);
2949 FreeParsedMessage(&(action->u.cmd.execHosts));
2950 for (i = 0; i < action->u.cmd.execHostCount; i++)
2951 XtFree(action->u.cmd.execHostArray[i]);
2952 if (action->u.cmd.execHostArray) {
2953 XtFree((char *)action->u.cmd.execHostArray);
2956 else if (IS_TT_MSG(action->mask))
2958 FreeParsedMessage(&(action->u.tt_msg.tt_op));
2959 FreeParsedMessage(&(action->u.tt_msg.tt_file));
2960 if (action->u.tt_msg.tt_argn_mode) {
2961 XtFree((char *)action->u.tt_msg.tt_argn_mode);
2963 FreeParsedMessageArray(action->u.tt_msg.tt_argn_vtype,
2964 action->u.tt_msg.vtype_count);
2965 FreeParsedMessageArray(action->u.tt_msg.tt_argn_value,
2966 action->u.tt_msg.value_count);
2967 if (action->u.tt_msg.tt_argn_rep_type) {
2968 XtFree((char *)action->u.tt_msg.tt_argn_rep_type);
2972 XtFree((char *)action);
2978 parsedMsg * old_pmsg,
2979 parsedMsg * new_pmsg )
2983 MsgComponent * piece;
2984 MsgComponent * newPiece;
2986 new_pmsg->numMsgParts = old_pmsg->numMsgParts;
2987 if (old_pmsg->compiledMessage)
2990 * Some day these may not always be null-terminated strings
2992 new_pmsg->compiledMessage = (char *)XtMalloc(old_pmsg->msgLen);
2993 memcpy(new_pmsg->compiledMessage,
2994 old_pmsg->compiledMessage,
2996 new_pmsg->msgLen = old_pmsg->msgLen;
3000 new_pmsg->compiledMessage = NULL;
3001 new_pmsg->msgLen = 0;
3005 /* Clone the message components */
3006 if (old_pmsg->numMsgParts > 0)
3008 new_pmsg->parsedMessage = (MsgComponent *)
3009 XtMalloc((Cardinal)(sizeof(MsgComponent) * old_pmsg->numMsgParts));
3011 for (i = 0; i < old_pmsg->numMsgParts; i++)
3013 piece = &(old_pmsg->parsedMessage[i]);
3014 newPiece = &(new_pmsg->parsedMessage[i]);
3016 /* Clone each subcomponent of this message */
3017 if (piece->precedingText)
3018 newPiece->precedingText = XtNewString(piece->precedingText);
3020 newPiece->precedingText = NULL;
3023 newPiece->prompt = XtNewString(piece->prompt);
3025 newPiece->prompt = NULL;
3027 newPiece->keyword = piece->keyword;
3028 newPiece->argNum = piece->argNum;
3029 newPiece->mask = piece->mask;
3033 new_pmsg->parsedMessage = NULL;
3038 * Free up the contents of a parsedMsg structure, but not the structure
3039 * itself (since many of our structures contain in-line instances of
3040 * the parsedMsg structure).
3044 parsedMsg * parsedMessage )
3049 /* Free up the message components */
3050 if (parsedMessage->numMsgParts > 0)
3052 for (i = 0; i < parsedMessage->numMsgParts; i++)
3054 XtFree(parsedMessage->parsedMessage[i].precedingText);
3055 XtFree(parsedMessage->parsedMessage[i].prompt);
3057 XtFree((char *)parsedMessage->parsedMessage);
3060 XtFree(parsedMessage->compiledMessage);
3065 * Allocate an array to hold a copy of all of the parsedMsg structures.
3066 * This array must be freed eventually by the caller.
3069 CloneParsedMessageArray(
3070 parsedMsg * pmsgArray,
3074 parsedMsg * newArray;
3080 newArray = (parsedMsg *)XtMalloc(sizeof(parsedMsg) * count);
3082 for (i = 0; i < count; i++)
3083 CloneParsedMessage(pmsgArray + i, newArray + i);
3090 * Free up the counted array of parsedMsg structures.
3091 * The array pointing to them also needs to be freed.
3094 FreeParsedMessageArray(
3095 parsedMsg * parsedMessageArray,
3101 for (i = 0; i < count; i++)
3102 FreeParsedMessage(parsedMessageArray + i);
3104 XtFree((char *)parsedMessageArray);
3108 /***************************************************************************/
3109 /***************************************************************************/
3110 /* Functions For Placing Arguments Into A Message String */
3111 /***************************************************************************/
3112 /***************************************************************************/
3116 * This function takes a 'parsedMsg' structure, and compiles all of its
3117 * pieces into a single string, replacing keywords as they are encountered.
3118 * Since a given action request can be made up of multiple pieces, this
3119 * function uses some static variables to maintain state information between
3120 * calls for the same action request; passing in 'True' for the 'initialize'
3121 * parameter for the first call for a given action request will clear out
3122 * any old static values.
3126 _DtCompileMessagePiece(
3128 ActionRequest *request,
3133 unsigned long processingMask,
3134 Boolean ** paramUsed,
3135 int * promptDataIndex )
3139 Boolean firstParmUsed;
3140 MsgComponent * segment;
3141 char * compiledMsg = NULL;
3142 int compiledMsgSize = 0;
3143 ObjectData tmpObjData;
3144 static char *sessionHostName= NULL;
3145 static char *displayHostName = NULL;
3146 static char *localHostName = NULL;
3148 XtFree(piece->compiledMessage);
3149 piece->compiledMessage = NULL;
3155 * Keep track of which parameters have been used, so that when
3156 * a %Args% keyword is encountered, we know which parameters
3157 * should be substituted.
3159 *promptDataIndex = 0;
3161 if (request->numObjects > 0) {
3162 *paramUsed = (Boolean *)XtMalloc((Cardinal)(sizeof(Boolean) *
3163 request->numObjects));
3164 for (i = 0; i < request->numObjects; i++)
3165 (*paramUsed)[i] = False;
3169 _DtSvcProcessLock();
3170 /* We need to query our hostname the first time only */
3171 if ( ! localHostName )
3172 localHostName = _DtGetLocalHostName();
3175 * Determine the display host name -- default to localHostName for
3176 * degenerate display names (i.e. :0, unix:0, local:0, ...)
3178 if ( ! displayHostName )
3179 displayHostName = _DtGetDisplayHostName(XtDisplay(w));
3181 if ( ! sessionHostName )
3182 sessionHostName = _DtGetSessionHostName();
3183 _DtSvcProcessUnlock();
3186 * The message is constructed by taking each of the
3187 * action segments, replacing any keywords, and then adding the
3188 * information to the end of the buffer.
3190 for (i = 0; i < piece->numMsgParts; i++)
3192 segment = piece->parsedMessage + i;
3194 /* Add any text preceding the keyword */
3195 if (segment->precedingText)
3197 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3198 (int)strlen(segment->precedingText));
3199 (void)strcat(compiledMsg, segment->precedingText);
3202 /* Process the keyword */
3203 switch (segment->keyword)
3207 /* Add in the local host name */
3208 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3209 (int)strlen(localHostName));
3210 (void)strcat(compiledMsg, localHostName);
3217 * Add in the host associated with the DB file from which this
3218 * action was loaded.
3223 fullPath = _DtDbPathIdToString(request->clonedAction->file_name_id);
3224 host = _DtHostString(fullPath);
3227 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3228 host ? (int)strlen(host) : 0);
3229 (void)strcat(compiledMsg, host);
3239 * Use the displayHostName determined the first time thru
3241 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3242 (int)strlen(displayHostName));
3243 (void)strcat(compiledMsg, displayHostName);
3250 * Add in the session server host where providing the
3251 * display management. (i.e. the host where the login client
3254 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3255 (int)strlen(sessionHostName));
3256 (void)strcat(compiledMsg, sessionHostName);
3264 * If this is an entry which simply collected some user input,
3265 * then add the user's input to the message buffer.
3266 * This corresponds to the keywords:
3269 * %(String)"prompt"%
3271 if (segment->prompt)
3273 /* Create dummy object; makes processing easier */
3274 if (ParseFileArgument(w, request, &tmpObjData,
3275 NULL, request->promptInputs[*promptDataIndex],
3278 XtFree(compiledMsg);
3282 if (!InsertArgumentString(w, &compiledMsg, &compiledMsgSize,
3283 request, &tmpObjData, segment->mask, relPathHost,
3284 relPathDir, False, 0))
3286 XtFree(compiledMsg);
3290 /* Signal that this prompt has been used */
3291 (*promptDataIndex)++;
3298 if (segment->argNum == ALL_ARGS)
3300 /* Insert all currently unused parameters */
3301 for (j = 0, firstParmUsed = False; j < request->numObjects; j++)
3303 /* Used or empty objects are skipped */
3304 if ((*paramUsed)[j] == False)
3306 if (IS_FILE_OBJ(request->objects[j].mask) &&
3307 request->objects[j].u.file.origFilename)
3309 if (!InsertArgumentString(w, &compiledMsg,
3311 request, request->objects+j,
3312 segment->mask, relPathHost, relPathDir,
3313 firstParmUsed, processingMask))
3315 XtFree(compiledMsg);
3318 firstParmUsed = True;
3322 * Since we use tmp files for buffers and do not support
3323 * strings; we need not add special code for buffer and
3326 /* fdt: add support for buffers and strings
3327 * else if (IS_BUFFER_OBJ(request->objects[i].mask) &&
3328 * request->objects[i].u.buffer.buffer)
3329 * else if (IS_STRING_OBJ(request->objects[i].mask) &&
3330 * request->objects[i].u.string.string)
3335 else if (segment->argNum <= request->numObjects)
3337 if (IS_FILE_OBJ(request->objects[segment->argNum-1].mask) &&
3338 request->objects[segment->argNum-1].u.file.origFilename)
3340 /* Replace only with the specified argument */
3341 (*paramUsed)[segment->argNum-1] = True;
3343 * All buffer objects have been written to tmp files.
3344 * This code replaces a reference to an object with its
3346 * Tooltalk processing code elsewhere
3347 * (ActionTt.c) detects the conditions under which a buffer
3348 * object reference should be replaced by the buffer contents
3349 * instead of the tmp file name. (i.e. a value field with a
3350 * single argument reference with no additional text). In such
3351 * cases the compiled message string will be ignored.
3353 if (!InsertArgumentString(w, &compiledMsg, &compiledMsgSize,
3354 request, request->objects + segment->argNum - 1,
3355 segment->mask, relPathHost, relPathDir, False,
3358 XtFree(compiledMsg);
3364 * Since we use tmp files for buffers and do not support
3365 * strings; we need not add special code for buffer and
3368 /* fdt: add support for buffers and strings
3369 * else if (IS_BUFFER_OBJ(request->objects[i].mask) &&
3370 * request->objects[i].u.buffer.buffer)
3371 * else if (IS_STRING_OBJ(request->objects[i].mask) &&
3372 * request->objects[i].u.string.string)
3380 if ((piece->compiledMessage = compiledMsg) == NULL)
3383 piece->msgLen = compiledMsg ? strlen(compiledMsg) + 1: 0;
3388 * Given an object, add it to the end of the message buffer. The
3389 * object may refer to a file, thus possibly requiring that it be
3390 * converted to another format.
3394 InsertArgumentString(
3398 ActionRequest *request,
3403 Boolean addLeadingSpace,
3404 unsigned long processingMask )
3415 if (processingMask & _DTAct_TT_VTYPE)
3416 SET_TREAT_AS_FILE(mask);
3418 if (IS_TREAT_AS_FILE(mask))
3420 if (object->type == -1)
3422 /* Object still needs to be typed */
3423 if (IS_FILE_OBJ(object->mask))
3425 char * origInfo = object->u.file.origFilename;
3427 ParseFileArgument(w, request, object, NULL, origInfo, NULL, True);
3432 * Since we use tmp files for buffers and do not support
3433 * strings; we need not add special code for buffer and
3436 /* fdt: add support for buffers and strings
3437 * else if (IS_BUFFER_OBJ(object->mask))
3438 * else if (IS_STRING_OBJ(object->mask))
3442 if (IS_FILE_OBJ(object->mask))
3444 if (processingMask & _DTAct_TT_VTYPE)
3447 * Instead of inserting the object referred to by "Arg_n",
3448 * we need to instead insert the MEDIA attribute for the
3449 * object. If the MEDIA attribute is not defined for the
3450 * datatype associated with this object, then use the
3451 * datatype name itself. If the thing can't be defined, then
3454 if (object->type != (-1))
3456 dataType = (char *)_DtDtsMMBosonToString(object->type);
3458 if ((path = _DtActMapFileName(
3459 request->hostNames[object->u.file.hostIndex],
3460 request->dirNames[object->u.file.dirIndex],
3461 object->u.file.baseFilename,
3467 mediaAttr = DtDtsDataTypeToAttributeValue(dataType, "MEDIA",
3473 value = XtNewString(mediaAttr);
3474 DtDtsFreeAttributeValue(mediaAttr);
3477 value = XtNewString(dataType);
3479 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(value) + 1);
3480 if (addLeadingSpace)
3481 strcat(*bufPtr, " ");
3482 strcat(*bufPtr, value);
3488 if (IS_CMD(request->clonedAction->mask))
3490 /* Map into a real path, relative to the execution host */
3491 if ((path = _DtActMapFileName(
3492 request->hostNames[object->u.file.hostIndex],
3493 request->dirNames[object->u.file.dirIndex],
3494 object->u.file.baseFilename,
3495 request->currentHost)) == NULL)
3497 AddFailedHostToList(request, request->currentHost);
3501 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(path) + 1);
3502 if (addLeadingSpace)
3503 (void)strcat(*bufPtr, " ");
3504 strcat(*bufPtr, path);
3507 else if (IS_TT_MSG(request->clonedAction->mask))
3510 * ToolTalk automatically translates the 'filename' field within
3511 * a message, and expects the incoming name to be relative to
3512 * the local host. So ... we simply need to map the name to
3513 * be relative to the local host. However, if this is not the
3514 * filename, but is instead one of the 'args', then we must
3515 * insert it in a 'neutral' form.
3517 if (processingMask & _DTAct_TT_ARG)
3519 /* Map into "host:/path" */
3520 /* fdt: May need to instead map into 'network indep' form */
3521 InsertUnmappedArgumentString(bufPtr, bufSizePtr, object,
3526 if ((path = _DtActMapFileName(
3527 request->hostNames[object->u.file.hostIndex],
3528 request->dirNames[object->u.file.dirIndex],
3529 object->u.file.baseFilename, NULL)) == NULL)
3534 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(path) + 1);
3535 if (addLeadingSpace)
3536 (void)strcat(*bufPtr, " ");
3537 strcat(*bufPtr, path);
3544 * Since we use tmp files for buffers and do not support
3545 * strings; we need not add special code for buffer and
3548 /* fdt: add support for buffers and strings
3549 * else if (IS_BUFFER_OBJ(object->mask))
3550 * else if (IS_STRING_OBJ(object->mask))
3554 InsertUnmappedArgumentString(bufPtr, bufSizePtr, object, addLeadingSpace);
3560 * This function knows how to insert a string in "host:/path" format;
3561 * this is essentually an 'unmapped' filename. File arguments which
3562 * have been preceded by the "(String)" qualifier will be saved in
3563 * this fashion. Likewise, any filenames (either in "String" or "File"
3564 * form) for an message will be saved in this format, due to the
3565 * fact that we don't know the execution host, and thus cannot properly
3566 * map the filename using the ToolTalk filename mapping functions.
3570 InsertUnmappedArgumentString(
3574 Boolean addLeadingSpace )
3580 /* No mapping is necessary here. */
3581 if (IS_FILE_OBJ(object->mask))
3583 size = strlen(object->u.file.origFilename) + 4;
3584 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, size);
3585 if (addLeadingSpace)
3586 (void)strcat(*bufPtr, " ");
3587 strcat(*bufPtr, object->u.file.origFilename);
3591 * Since we use tmp files for buffers and do not support
3592 * strings; we need not add special code for buffer and
3595 /* fdt: add support for buffers and strings
3596 * else if (IS_BUFFER_OBJ(object->mask))
3597 * else if (IS_STRING_OBJ(object->mask))
3603 * This function checks to see if the message buffer is large enough
3604 * to hold the current contents + 'count' more bytes. If it is not
3605 * large enough, then the buffer will be grown. The buffer MUST BE
3616 int currentBufUsed = buffer ? strlen(buffer) : 0;
3618 if ((currentBufUsed + count + 1) >= *size)
3620 (*size) += (count+1 > 1024) ? count + 1 : 1024;
3621 buffer = (char *)XtRealloc(buffer, (Cardinal)*size);
3623 /* If this is the first alloc for the buffer, then terminate the buffer */
3624 if(currentBufUsed == 0)
3633 /***************************************************************************/
3634 /***************************************************************************/
3635 /* Functions For Matching Arguments To A Message String */
3636 /***************************************************************************/
3637 /***************************************************************************/
3641 * If the specified prompt has not already been added to the array of
3642 * prompt strings, then add it. The exception is for stand-alone
3643 * prompt strings, which always are added.
3651 PromptEntry **prompts )
3657 * Standard arguments only want their prompts entered once.
3658 * Stand-alone prompts all have argNum == NO_ARG, and each one
3659 * must be saved. It's a special case.
3661 if (argNum != NO_ARG)
3663 for (i = 0; i < *numPrompts; i++)
3665 if ((*prompts)[i].argIndex == argNum)
3671 *prompts = (PromptEntry *)XtRealloc((char *)*prompts,
3672 (Cardinal)(sizeof(PromptEntry) * *numPrompts));
3673 (*prompts)[(*numPrompts) - 1].argIndex = argNum;
3674 (*prompts)[(*numPrompts) - 1].prompt = prompt;
3679 * This function takes an action DB entry and an action request, and
3680 * determines if enough information was supplied to create the message
3681 * needed to get the work done. If information was missing, then this
3682 * function will return an array of prompt strings, which can be used
3683 * to create a dialog for collecting the missing information. This
3684 * function also returns an indication of how many of the parameters
3687 * The caller is responsible for freeing up the prompt array, but the
3688 * entries in the array MUST NOT be freed up.
3692 MatchParamsToAction(
3693 ActionRequest *request,
3695 PromptEntry **prompts )
3697 Boolean * paramUsed = NULL;
3699 Boolean argsOptionFound;
3701 int lastArgReferenced;
3702 ActionPtr action = request->clonedAction;
3704 /* Initialize things */
3707 argsOptionFound = False;
3708 lastArgReferenced = -1;
3711 * This array lets us know which parameters can be used when we
3712 * encounter the %Args% keyword.
3714 unused = request->numObjects;
3716 paramUsed = (Boolean *)XtMalloc((Cardinal)(sizeof(Boolean) * unused));
3717 for (i = 0; i < unused; i++)
3718 paramUsed[i] = False;
3721 if (IS_CMD(action->mask))
3724 * NOTE: The current implementation of prompt strings requires that
3725 * the segments be evaluated in the same order in which the
3726 * message fields were parsed.
3727 * (See ResolveCommandInvokerMessagePieces() )
3728 * This order is currently "execHost", "execString" and
3729 * "termOpts". This situation arises because
3730 * the existing prompt data structures do NOT identify the
3731 * location of the prompt and hence where to put the
3732 * user-supplied value; except by order of occurrence.
3734 ProcessOneSegment(request, &(action->u.cmd.execHosts), prompts,
3735 numPrompts, &argsOptionFound, &lastArgReferenced,
3736 &unused, paramUsed);
3737 ProcessOneSegment(request, &(action->u.cmd.execString), prompts,
3738 numPrompts, &argsOptionFound, &lastArgReferenced,
3739 &unused, paramUsed);
3740 ProcessOneSegment(request, &(action->u.cmd.termOpts), prompts,
3741 numPrompts, &argsOptionFound, &lastArgReferenced,
3742 &unused, paramUsed);
3744 else if (IS_TT_MSG(action->mask))
3746 ProcessOneSegment(request, &(action->u.tt_msg.tt_op), prompts,
3747 numPrompts, &argsOptionFound, &lastArgReferenced,
3748 &unused, paramUsed);
3749 ProcessOneSegment(request, &(action->u.tt_msg.tt_file), prompts,
3750 numPrompts, &argsOptionFound, &lastArgReferenced,
3751 &unused, paramUsed);
3753 for (i = 0; i < action->u.tt_msg.vtype_count; i++)
3755 ProcessOneSegment(request, &(action->u.tt_msg.tt_argn_vtype[i]),
3756 prompts, numPrompts, &argsOptionFound,
3757 &lastArgReferenced, &unused, paramUsed);
3760 for (i = 0; i < action->u.tt_msg.value_count; i++)
3763 * We require that at most ONE argument be consumed by a
3764 * tt_argn_value field.
3766 ProcessOneSegment(request, &(action->u.tt_msg.tt_argn_value[i]),
3767 prompts, numPrompts, &argsOptionFound,
3768 &lastArgReferenced, &unused, paramUsed);
3774 * Now that we have processed all of the pieces which will ultimately
3775 * used to construct our message, determine if any of the arguments
3776 * passed to _DtActionInvoke were not used; this allows us to tell
3777 * the user that there were unused arguments, so they can choose
3778 * to continue or abort the request.
3779 * If we ever encountered a %Args% keyword, then ultimately all of
3780 * the parameters will be used.
3782 if (argsOptionFound)
3787 * Determine how many arguments were actually unused; only count
3788 * those arguments AFTER the last referenced one. i.e. if arg2
3789 * is referenced, but arg1 and arg3 are not, then only count arg3
3790 * as an unused (and thus extra) parameter.
3792 for (i = 0; ((i < lastArgReferenced - 1) && (i < request->numObjects));
3799 /* This should never happen, but ... */
3804 if (paramUsed) XtFree(paramUsed);
3812 ActionRequest * request,
3814 PromptEntry **prompts,
3816 Boolean * argsOptionFound,
3817 int * lastArgReferenced,
3819 Boolean * paramUsed )
3821 MsgComponent * piece;
3825 * Check each piece of this message component, to see if the parameter
3826 * it expects has been supplied. If the parameter is missing, and
3827 * a prompt was supplied, then add the prompt to the prompt array.
3829 for (i = 0; i < msg->numMsgParts; i++)
3831 piece = msg->parsedMessage + i;
3834 * We only care about %Args% and %Arg_<n>% keywords, and
3835 * entries which have no keyword, but do have a prompt.
3837 if (piece->keyword == ARG)
3839 if (piece->argNum == ALL_ARGS)
3842 * When a %Args% keyword is found, this implies that there
3843 * will ultimately be no unused parameters, because this
3844 * keyword is replaced by all unused parameters.
3846 *argsOptionFound = True;
3848 else if (piece->argNum > 0)
3850 /* Keep track of the largest arg index referenced */
3851 if (piece->argNum > *lastArgReferenced)
3852 *lastArgReferenced = piece->argNum;
3854 /* See if a parameter was supplied for this argNum */
3855 if (piece->argNum > request->numObjects)
3857 /* Parameter is missing; see if a prompt was given */
3859 AddPrompt(piece->argNum, piece->prompt, numPrompts, prompts);
3863 /* Mark this parameter as having been used */
3864 if (!paramUsed[piece->argNum - 1])
3866 paramUsed[piece->argNum - 1] = True;
3872 else if ((piece->keyword == NO_KEYWORD) && (piece->prompt))
3874 /* Entries may be nothing but a prompt */
3875 AddPrompt(NO_ARG, piece->prompt, numPrompts, prompts);
3880 /***************************************************************************/
3881 /***************************************************************************/
3882 /* Prompt Dialog Support */
3883 /***************************************************************************/
3884 /***************************************************************************/
3888 * This is the event handler which catches the 'escape' key when typed
3889 * into the prompt. It will unpost the dialog.
3897 XtPointer num_params)
3902 /* Get the cancel button widget id */
3903 XtSetArg(args[0], XmNuserData, &cancel);
3904 XtGetValues(w, args, 1);
3906 /* Unpost the text annotation dialog */
3907 XtCallCallbacks(cancel, XmNactivateCallback, NULL);
3912 * 'Cancel' callback for the dialog used to collect missing parameters
3913 * from the user. It will free up the memory holding the cancelled
3914 * request and will destroy the dialog.
3920 PromptDialog *dialog,
3921 XtPointer call_data )
3924 unsigned long evalStatus;
3925 unsigned long userStatus;
3926 _DtActInvRecT *invp;
3928 /* Destroy the dialog */
3929 XtDestroyWidget(XtParent(dialog->topLevel));
3931 /* Free up the prompt sub-structure */
3932 XtFree((char *)dialog->prompts);
3935 invp = _DtActFindInvRec(dialog->request->invocId);
3936 myassert(invp); /* There should always be an invocation record */
3938 /* Free up the original request structure */
3939 _DtFreeRequest(dialog->request);
3941 /* Free up the callback structure */
3942 XtFree((char *)dialog);
3945 return; /* This should never happen */
3947 SET_INV_CANCEL(invp->state);
3950 * Evaluate whether we are done with this invocation -- are there
3951 * uncompleted children? There should not be any subsequent invocations
3952 * to worry about since this cancel effectively aborts further processing.
3954 * We may have to return values to the caller.
3957 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
3962 * This function changes the focus from the given "widget's"
3963 * tab group to the next tab group.
3967 ChangePromptTraversal(
3969 PromptDialog *dialog,
3970 XtPointer call_data )
3972 XmProcessTraversal (widget, XmTRAVERSE_NEXT_TAB_GROUP);
3976 * 'Ok' callback for the dialog used to collect missing parameters
3977 * from the user. It will redo the array of parameter strings, and
3978 * then execute the command, given whatever the user has supplied.
3979 * It will also destroy the dialog box.
3983 ProcessPromptDialog(
3985 PromptDialog *dialog,
3986 XtPointer call_data )
3992 /* Unpost the dialog */
3993 XtUnmanageChild(dialog->topLevel);
3996 * Given the set of strings supplied by the user, update the
3997 * object array which is part of the original request.
3999 for (i = 0; i < dialog->numPrompts; i++)
4001 value = XmTextFieldGetString(dialog->prompts[i].promptWidget);
4003 /* Do we need to grow the object array? */
4004 if (dialog->prompts[i].argIndex > 0)
4006 if (_DtEmptyString(value))
4012 if (dialog->prompts[i].argIndex > dialog->request->numObjects)
4014 dialog->request->objects = (ObjectData *)
4015 XtRealloc((char *)dialog->request->objects,
4016 (Cardinal)(sizeof(ObjectData) * (dialog->prompts[i].argIndex)));
4018 /* Initialize the new array entries */
4019 for (j = dialog->request->numObjects;
4020 j < dialog->prompts[i].argIndex;
4023 dialog->request->objects[j].mask = 0;
4024 SET_FILE_OBJ(dialog->request->objects[j].mask);
4025 dialog->request->objects[j].type = -1;
4026 dialog->request->objects[j].u.file.hostIndex = -1;
4027 dialog->request->objects[j].u.file.dirIndex = -1;
4028 dialog->request->objects[j].u.file.origFilename = NULL;
4029 dialog->request->objects[j].u.file.origHostname = NULL;
4030 dialog->request->objects[j].u.file.baseFilename = NULL;
4031 dialog->request->objects[j].u.file.bp = 0;
4032 dialog->request->objects[j].u.file.sizebp = 0;
4033 dialog->request->objects[j].u.buffer.bp = 0;
4034 dialog->request->objects[j].u.buffer.size = 0;
4035 dialog->request->objects[j].u.string.string = 0;
4036 SET_UNKNOWN_IF_DIR(dialog->request->objects[j].mask);
4038 dialog->request->numObjects = dialog->prompts[i].argIndex;
4041 * These values cannot be broken up into host/dir/file components,
4042 * nor can they be typed, until we know it they refer to a file.
4043 * This can't be determined until we construct the action message.
4045 dialog->request->objects[dialog->prompts[i].argIndex-1].u.file.
4046 origFilename = value;
4048 else /* Prompt-only input */
4051 * Prompt-only input can't fit in our ordered object array,
4052 * since they don't have a unique argIndex which can be used
4053 * as the index into the object array.
4055 dialog->request->numPromptInputs++;
4056 dialog->request->promptInputs = (String *)
4057 XtRealloc((char *)dialog->request->promptInputs,
4058 (Cardinal)(dialog->request->numPromptInputs * sizeof(String)));
4060 dialog->request->promptInputs[dialog->request->numPromptInputs-1] =
4065 /* Destroy the dialog */
4066 XtDestroyWidget(XtParent(dialog->topLevel));
4067 XmUpdateDisplay(widget);
4070 * Invoke the action using the information we've collected.
4071 * If this was a single argument action, then the reprocessing
4072 * of it may have generated another dialog, so we can only free
4073 * up the request, when all processing is done.
4075 if (ProcessRequest(dialog->associatedWidget, dialog->request))
4077 _DtActInvRecT *invp;
4078 unsigned long evalStatus;
4079 unsigned long userStatus;
4081 if ( (invp = _DtActFindInvRec(dialog->request->invocId)) )
4083 /* all done invoking ? */
4084 RESET_INV_PENDING(invp->state);
4086 /* We should only get here if all requests have been honored */
4087 SET_INV_COMPLETE(invp->state);
4090 * evaluate whether all child actions have been completed
4091 * and if its time to call the user callback.
4093 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
4095 myassert(invp); /* there should always be one to find */
4096 _DtFreeRequest(dialog->request);
4099 /* Free up the prompt sub-structure */
4100 XtFree((char *)dialog->prompts);
4102 /* Free up the callback structure */
4103 XtFree((char *)dialog);
4108 * This function takes the array of prompt strings, and creates a
4109 * dialog box, using these prompt strings as the labels for a set
4116 ActionRequest *request,
4118 PromptEntry *prompts )
4121 PromptDialog * dialog;
4122 DialogPromptEntry * promptDes;
4125 Widget shell, bboard, frame, form, label;
4126 Widget promptLabel, topAttach;
4127 Widget separator, ok, cancel;
4131 XmString labelString;
4132 XWindowAttributes xwa;
4134 Boolean is_mapped = False;
4135 static XtTranslations trans_table;
4136 static Boolean first = True;
4137 Atom xa_WM_DELETE_WINDOW;
4140 * Want to set up the Escape key so that it will unpost the dialog.
4142 _DtSvcProcessLock();
4145 XtAppAddActions(XtWidgetToApplicationContext(w), actionTable, 1);
4146 trans_table = XtParseTranslationTable(translations_escape);
4149 _DtSvcProcessUnlock();
4151 /* Allocate the structures we'll be needing */
4152 dialog = (PromptDialog *)XtMalloc((Cardinal)sizeof(PromptDialog));
4153 promptDes = (DialogPromptEntry *)XtMalloc((Cardinal)
4154 (sizeof(DialogPromptEntry) * numPrompts));
4157 /* Create the shell, frame and form used for the dialog. */
4159 title = (char *)XtMalloc((Cardinal)
4160 (strlen(PromptDialogTitle)+ strlen(request->clonedAction->label) + 1));
4161 (void)sprintf(title, "%1$s%2$s", PromptDialogTitle, request->clonedAction->label);
4163 XtSetArg (args[n], XmNallowShellResize, True); n++;
4164 XtSetArg (args[n], XmNtitle, title); n++;
4165 shell = XmCreateDialogShell (w, "promptDialog", args, n);
4168 if (XtIsRealized(w))
4170 status = XGetWindowAttributes (XtDisplay (w), XtWindow (w), &xwa);
4171 if (status && (xwa.map_state == IsViewable))
4176 XtSetArg (args[n], XmNmarginWidth, 0); n++;
4177 XtSetArg (args[n], XmNmarginHeight, 0); n++;
4180 XtSetArg (args[n], XmNdefaultPosition, False);
4183 bboard = XmCreateBulletinBoard (shell, "bboard", args, n);
4186 XtSetArg (args[n], XmNshadowThickness, 1); n++;
4187 XtSetArg (args[n], XmNshadowType, XmSHADOW_OUT); n++;
4188 frame = XmCreateFrame (bboard, "frame", args, n);
4189 XtManageChild (frame);
4192 XtSetArg (args[n], XmNautoUnmanage, False); n++;
4193 XtSetArg (args[n], XmNtextTranslations, trans_table); n++;
4194 form = XmCreateForm (frame, "form", args, n);
4195 XtManageChild (form);
4197 /* Create the dialog description label */
4199 pt1 = XmStringCreateLocalized(PromptDialogLabel);
4201 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
4202 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
4203 XtSetArg(args[n], XmNleftOffset, 20); n++;
4204 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
4205 XtSetArg(args[n], XmNrightOffset, 20); n++;
4206 XtSetArg(args[n], XmNtopOffset, 15); n++;
4207 XtSetArg(args[n], XmNlabelString, pt1); n++;
4208 label = XmCreateLabelGadget(form, "label", args, n);
4209 XtManageChild (label);
4212 /* Create each of the needed prompts */
4214 for (count = 0; count < numPrompts; count++)
4216 promptDes[count].argIndex = prompts[count].argIndex;
4218 pt1 = XmStringCreateLocalized(prompts[count].prompt);
4220 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4221 XtSetArg(args[n], XmNtopWidget, topAttach); n++;
4222 XtSetArg(args[n], XmNtopOffset, 10); n++;
4223 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
4224 XtSetArg(args[n], XmNleftOffset, 30); n++;
4225 XtSetArg(args[n], XmNlabelString, pt1); n++;
4226 promptLabel = XmCreateLabelGadget(form, "promptLabel", args, n);
4227 XtManageChild(promptLabel);
4231 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4232 XtSetArg(args[n], XmNtopWidget, topAttach); n++;
4233 XtSetArg(args[n], XmNtopOffset, 8); n++;
4234 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
4235 XtSetArg(args[n], XmNrightOffset, 30); n++;
4236 XtSetArg(args[n], XmNtraversalOn, True); n++;
4237 XtSetArg(args[n], XmNleftAttachment,XmATTACH_WIDGET ); n++;
4238 XtSetArg(args[n], XmNleftWidget, promptLabel); n++;
4239 XtSetArg(args[n], XmNleftOffset, 15); n++;
4240 promptDes[count].promptWidget = XmCreateTextField(form, "text", args, n);
4242 XtManageChild(promptDes[count].promptWidget);
4244 XmAddTabGroup(promptDes[count].promptWidget);
4245 topAttach = promptDes[count].promptWidget;
4249 /* Create a separator between the buttons */
4252 XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
4253 XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
4254 XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4255 XtSetArg (args[n], XmNtopWidget, topAttach); n++;
4256 XtSetArg (args[n], XmNtopOffset, 20); n++;
4257 separator = XmCreateSeparatorGadget (form, "separator", args, n);
4258 XtManageChild (separator);
4261 /* Create the ok and cancel buttons */
4264 labelString = XmStringCreateLocalized((String)_DtOkString);
4265 XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
4266 XtSetArg (args[n], XmNleftPosition, 5 + 10); n++;
4267 XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
4268 XtSetArg (args[n], XmNrightPosition, 31 + 10); n++;
4269 XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4270 XtSetArg (args[n], XmNtopWidget, separator); n++;
4271 XtSetArg (args[n], XmNtopOffset, 16); n++;
4272 XtSetArg (args[n], XmNbottomOffset, 16); n++;
4273 XtSetArg (args[n], XmNmarginHeight, 4); n++;
4274 XtSetArg (args[n], XmNshowAsDefault, True); n++;
4275 XtSetArg (args[n], XmNlabelString, labelString); n++;
4276 ok = XmCreatePushButtonGadget (form, "ok", args, n);
4278 XtAddCallback(ok, XmNactivateCallback, (XtCallbackProc)ProcessPromptDialog,
4280 XmStringFree(labelString);
4282 /* Set the default action */
4284 if (numPrompts <= 1)
4287 XtSetArg (args[n], XmNdefaultButton, ok); n++;
4288 XtSetValues(bboard, args, n);
4294 * Want to set the traversal so that if "return" is hit in the
4295 * last prompt, the "ProcessPromptDialog" callback is invoked.
4296 * Otherwise, the "return" should move the focus to the next
4297 * which is in a different (tab group).
4299 for (i = 0; i < numPrompts; i++)
4301 if (i <= (numPrompts - 2))
4302 XtAddCallback(promptDes[i].promptWidget, XmNactivateCallback,
4303 (XtCallbackProc)ChangePromptTraversal,
4306 XtAddCallback(promptDes[i].promptWidget, XmNactivateCallback,
4307 (XtCallbackProc)ProcessPromptDialog,
4313 labelString = XmStringCreateLocalized((String)_DtCancelString);
4314 XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
4315 XtSetArg (args[n], XmNleftPosition, 37 + 22); n++;
4316 XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
4317 XtSetArg (args[n], XmNrightPosition, 63 + 22); n++;
4318 XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4319 XtSetArg (args[n], XmNtopWidget, separator); n++;
4320 XtSetArg (args[n], XmNtopOffset, 21); n++;
4321 XtSetArg (args[n], XmNbottomOffset, 21); n++;
4322 XtSetArg (args[n], XmNmarginHeight, 4); n++;
4323 XtSetArg (args[n], XmNlabelString, labelString); n++;
4324 cancel = XmCreatePushButtonGadget (form, "cancel", args, n);
4325 XtManageChild(cancel);
4326 XtAddCallback(cancel, XmNactivateCallback, (XtCallbackProc)CancelPromptDialog,
4328 XmStringFree(labelString);
4331 * For each prompt, must set up the Escape key to be equivalent
4332 * to the "Cancel button.
4334 for (i = 0; i < numPrompts; i++) {
4336 XtSetArg(args[n], XmNuserData, cancel); n++;
4337 XtSetValues(promptDes[i].promptWidget, args, n);
4341 * If the widget is not mapped, center this dialog.
4345 Dimension dialogWd, dialogHt;
4347 XtSetArg(args[0], XmNmappedWhenManaged, False);
4348 XtSetValues(shell, args, 1);
4350 XtManageChild(bboard);
4351 XtRealizeWidget(shell);
4353 XtSetArg(args[0], XmNwidth, &dialogWd);
4354 XtSetArg(args[1], XmNheight, &dialogHt);
4355 XtGetValues(bboard, args, 2);
4357 XtSetArg (args[0], XmNx,
4358 (WidthOfScreen(XtScreen(bboard)) - dialogWd) / 2U);
4359 XtSetArg (args[1], XmNy,
4360 (HeightOfScreen(XtScreen(bboard)) - dialogHt) / 2U);
4361 XtSetValues (bboard, args, 2);
4364 /* Adjust the decorations for the dialog shell of the dialog */
4367 XtSetArg (args[n], XmNmwmDecorations,
4368 MWM_DECOR_BORDER | MWM_DECOR_MENU | MWM_DECOR_TITLE); n++;
4369 XtSetValues(shell, args, n);
4371 xa_WM_DELETE_WINDOW =
4372 XInternAtom(XtDisplay(shell), "WM_DELETE_WINDOW", False);
4373 XmAddWMProtocolCallback(
4374 shell, xa_WM_DELETE_WINDOW,
4375 (XtCallbackProc) CancelPromptDialog, (XtPointer) dialog);
4377 /* Fill in our instance structure */
4378 dialog->request = request;
4379 dialog->topLevel = bboard;
4380 dialog->numPrompts = count;
4381 dialog->prompts = promptDes;
4382 dialog->associatedWidget = w;
4384 /* Post the dialog */
4385 XtSetArg(args[0], XmNmappedWhenManaged, True);
4386 XtSetValues(shell, args, 1);
4387 XtManageChild(bboard);
4389 /* Make the first prompt automatically get the focus. */
4390 if (numPrompts >= 0)
4391 XmProcessTraversal(promptDes[0].promptWidget, XmTRAVERSE_CURRENT);
4395 /***************************************************************************/
4396 /***************************************************************************/
4397 /* Continue Dialog Support */
4398 /***************************************************************************/
4399 /***************************************************************************/
4403 * 'Ok' callback for the abort/continue dialog. It will continue with the
4404 * processing of the request, ignoring any unused parameters.
4410 XtPointer user_data,
4411 XtPointer call_data )
4415 ContinueDialog *dialog = (ContinueDialog *)user_data;
4417 /* Destroy the dialog */
4418 XtDestroyWidget(XtParent(dialog->topLevel));
4419 XmUpdateDisplay(widget);
4422 * If we need to collect some prompt input from the user, then
4423 * post the prompt dialog; otherwise, send the action request.
4425 if (dialog->numPrompts == 0)
4427 if (ProcessRequest(dialog->associatedWidget, dialog->request))
4429 _DtActInvRecT *invp;
4431 if((invp=_DtActFindInvRec(dialog->request->invocId))!=NULL)
4433 /* all done invoking ? */
4434 RESET_INV_PENDING(invp->state);
4436 /* We should only get here if all requests have been honored */
4437 SET_INV_COMPLETE(invp->state);
4440 * evaluate whether all child actions have been completed
4441 * and if its time to call the user callback.
4443 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
4446 myassert(invp); /* there should always be one to find */
4447 _DtFreeRequest(dialog->request);
4452 CreatePromptDialog(dialog->associatedWidget, dialog->request,
4458 /* Free up the prompt sub-structure */
4459 for (i = 0; i < dialog->numPrompts; i++)
4460 XtFree(dialog->prompts[i].prompt);
4461 XtFree((char *)dialog->prompts);
4463 /* Free up the callback structure */
4464 XtFree((char *)dialog);
4469 * 'Cancel' callback for the dialog which prompts the user to continue
4470 * or abort, when too many parameters have been supplied. This will
4471 * free up the dialog data and the request and will destroy the dialog.
4477 XtPointer user_data,
4478 XtPointer call_data )
4482 ContinueDialog *dialog = (ContinueDialog *)user_data;
4483 unsigned long evalStatus;
4484 unsigned long userStatus;
4485 _DtActInvRecT *invp;
4487 /* Destroy the dialog */
4488 XtDestroyWidget(XtParent(dialog->topLevel));
4490 /* Free up the prompt sub-structure */
4491 for (i = 0; i < dialog->numPrompts; i++)
4492 XtFree(dialog->prompts[i].prompt);
4493 XtFree((char *)dialog->prompts);
4495 /* get the invocation record */
4496 invp = _DtActFindInvRec(dialog->request->invocId);
4497 myassert(invp); /* There should always be one available */
4499 /* Free up the original request structure */
4500 _DtFreeRequest(dialog->request);
4502 /* Free up the callback structure */
4503 XtFree((char *)dialog);
4506 return; /* should never happen */
4508 SET_INV_CANCEL(invp->state);
4510 * Evaluate whether we are done with this invocation -- are there
4511 * uncompleted children? There should not be any subsequent invocations
4512 * to worry about since this cancel effectively aborts further processing.
4514 * We may have to return values to the caller.
4516 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
4520 * When an action is requested, and more parameters than are needed
4521 * are supplied, the user will be prompted to continue with the
4522 * operation (ignoring the extra parameters), or to abort the request.
4524 * This function builds the dialog which will collect the user's response.
4528 CreateContinueDialog(
4530 ActionRequest *request,
4532 PromptEntry *prompts )
4535 ContinueDialog * dialog;
4541 XmString ok, cancel;
4544 /* Allocate the structures we'll be needing */
4545 dialog = (ContinueDialog *)XtMalloc((Cardinal)sizeof(ContinueDialog));
4546 dialog->request = request;
4547 dialog->associatedWidget = w;
4548 dialog->numPrompts = numPrompts;
4551 * We need to make a clone of the prompt array, since the strings
4552 * it contains are not ones we can guarantee will be around when
4553 * the user finally responds to this dialog.
4557 dialog->prompts = (PromptEntry *)
4558 XtMalloc((Cardinal)(sizeof(PromptEntry) * numPrompts));
4559 for (i = 0; i < numPrompts; i++)
4561 dialog->prompts[i].argIndex = prompts[i].argIndex;
4562 dialog->prompts[i].prompt = XtNewString(prompts[i].prompt);
4566 dialog->prompts = NULL;
4568 ok = XmStringCreateLocalized((String)_DtOkString);
4569 cancel = XmStringCreateLocalized((String)_DtCancelString);
4571 /* Create the error dialog */
4572 fmt = XtNewString((char *)Dt11GETMESSAGE(2, 2, "%1$s%2$s"));
4573 title = (char *)XtMalloc((Cardinal)
4574 (strlen(PromptDialogTitle) +
4575 strlen(request->clonedAction->label) +
4577 (void)sprintf(title, fmt, PromptDialogTitle, request->clonedAction->label);
4578 label = XmStringCreateLocalized(ContinueMessage);
4582 XtSetArg(args[n], XmNmessageString, label); n++;
4583 XtSetArg(args[n], XmNtitle, title); n++;
4584 XtSetArg(args[n], XmNokLabelString, ok); n++;
4585 XtSetArg(args[n], XmNcancelLabelString, cancel); n++;
4586 dialog->topLevel = XmCreateWarningDialog(w, "continueDialog", args, n);
4589 XmStringFree(cancel);
4590 XmStringFree(label);
4592 XtUnmanageChild(XmMessageBoxGetChild(dialog->topLevel,
4593 XmDIALOG_HELP_BUTTON));
4594 XtAddCallback(dialog->topLevel, XmNokCallback, ContinueRequest,
4596 XtAddCallback(dialog->topLevel, XmNcancelCallback, CancelRequest,
4598 XtManageChild(dialog->topLevel);
4602 /***************************************************************************/
4603 /***************************************************************************/
4604 /* Command Invoker Specific Functions */
4605 /***************************************************************************/
4606 /***************************************************************************/
4609 * This is the entry point into the command-specific world. All of the code
4610 * before this has been written to handle any of the different transport
4611 * types. The code from this point on will know specifically how to
4612 * interact with the command invoker layer. We will start by taking each
4613 * of the pieces of information making up the command invoker request, and
4614 * resolving any of the keywords, by replacing them with the appropriate
4615 * information. If this fails (which it only should do if we try to map
4616 * a file to a host which cannot be accessed), then we will either continue,
4617 * using the next exec host, or we will terminate, if no more hosts are left.
4621 ProcessCommandInvokerRequest(
4623 ActionRequest *request,
4630 ActionPtr action = request->clonedAction;
4631 _DtActInvRecT *invp = NULL;
4632 _DtActChildRecT *childp = NULL;
4634 if (ResolveCommandInvokerMessagePieces(w, request, relPathHost, relPathDir))
4637 * Issue the request; the success/failure notification comes
4638 * asynchronously; that's when everything gets cleaned up, or
4639 * tried again, for the next exec host.
4641 __ExtractCWD(request, &cwdHost, &cwdDir, True);
4642 InitiateCommandInvokerRequest( w, request, cwdHost, cwdDir);
4648 if ( !(invp = _DtActFindInvRec(request->invocId) ) )
4649 myassert( 0 /* could not find invocation record */ );
4651 if ( !(childp=_DtActFindChildRec(request->invocId,request->childId)))
4652 myassert( 0 /* could not find child record */ );
4655 * The only way we could have reached here is if the execution host
4656 * was not accessible, and we tried to map one of the data files to
4657 * be relative to this host. If there are other hosts to be tried,
4658 * then we will retry the request on the next host; otherwise, we
4659 * will post an error dialog, and bail out.
4662 request->hostIndex++;
4664 if (request->hostIndex >= action->u.cmd.execHostCount)
4666 /* No more hosts to try; report an error, and bail out */
4668 if ( invp && childp )
4670 SET_INV_ERROR(invp->state);
4671 childp->childState = _DtActCHILD_FAILED;
4675 * Cleanup should happen later when we return up the stack.
4678 if (action->u.cmd.execHostCount <= 1)
4680 /* Display error dialog listing just the one failed exec host */
4681 HostAccessError(w, request->clonedAction->label, request->badHostList);
4685 /* Display error dialog listing all failed exec hosts */
4686 MultiHostAccessError(w, request->clonedAction->label, request->badHostList);
4691 if ( invp && childp )
4694 * Delete child record for failed exec on this host
4696 _DtActDeleteChildRec(invp,childp);
4697 SET_INV_PENDING(invp->state);
4698 if ( ! invp->numChildren )
4699 RESET_INV_WORKING(invp->state);
4702 /* Retry the request, using the next exec host */
4703 PrepareAndExecuteAction(w, request);
4711 * This function takes all of the pieces making up a command invoker request,
4712 * and resolves any references to keywords, using both the passed-in
4713 * arguments, and any information collected from the prompt dialog.
4717 ResolveCommandInvokerMessagePieces(
4719 ActionRequest *request,
4724 ActionPtr action = request->clonedAction;
4725 cmdAttr * cmd = &(action->u.cmd);
4727 Boolean * paramUsed = NULL;
4728 int promptDataIndex = 0;
4730 * NOTE: The current implementation of prompt strings requires that
4731 * the segments be evaluated in the same order in which the
4732 * action fields were parsed. (See MatchParamsToAction() )
4733 * This order is currently "execHost", "execString" and
4734 * "termOpts". This situation arises because
4735 * the existing prompt data structures do NOT identify the
4736 * location of the prompt and hence where to put the
4737 * user-supplied value; except by order of occurrence.
4740 /* Set up the next host to execute on */
4741 _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
4742 &(action->u.cmd.execHosts), True, 0, ¶mUsed,
4744 SetExecHost(request);
4746 if ((_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
4747 &(cmd->execString), False, 0, ¶mUsed,
4748 &promptDataIndex) == False) ||
4749 (_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
4750 &(cmd->termOpts), False, 0, ¶mUsed,
4751 &promptDataIndex) == False))
4753 /* Free up any intermediate work we've done here */
4754 XtFree(cmd->execString.compiledMessage);
4755 XtFree(cmd->termOpts.compiledMessage);
4756 XtFree(cmd->execHosts.compiledMessage);
4757 cmd->execString.compiledMessage = NULL;
4758 cmd->termOpts.compiledMessage = NULL;
4759 cmd->execHosts.compiledMessage = NULL;
4765 * If term_opts were passed in to the _DtActionInvoke() function, then
4766 * append them to the term_opts derived from the action definition and
4767 * internal defaults. This should give precedence to the last defined
4770 if ( request->termOpts )
4772 termOpts = XtMalloc( strlen(cmd->termOpts.compiledMessage) +
4773 strlen(request->termOpts) + 2 );
4774 strcpy(termOpts,cmd->termOpts.compiledMessage);
4775 strcat(termOpts," ");
4776 strcat(termOpts,request->termOpts);
4777 XtFree(cmd->termOpts.compiledMessage);
4778 cmd->termOpts.compiledMessage = termOpts;
4787 * Process a command-invoker request.
4791 InitiateCommandInvokerRequest(
4793 ActionRequest *request,
4798 char procIdBuf[_DtAct_MAX_BUF_SIZE];
4799 char tmpFileBuf[_DtAct_MAX_BUF_SIZE];
4800 char *procId; /* for dtexec command line */
4801 char *tmpFiles = NULL; /* for dtexec command line */
4802 _DtActInvRecT *invp;
4803 _DtActChildRecT *childp;
4804 CallbackData *data=(CallbackData *)XtMalloc((Cardinal)sizeof(CallbackData));
4805 ActionPtr action = request->clonedAction;
4807 tmpFileBuf[0]='\0'; /* seed the buffer with a null string */
4810 * Generate the procId option string for dtexec
4813 /* Get the default procId from toolTalk */
4814 switch ( tt_ptr_error(procId = tt_default_procid()) )
4817 ; /* fall through */
4818 case TT_ERR_PROCID: /* Try to establish a connection */
4820 if ( !_DtInitializeToolTalk(NULL) )
4822 else if ( tt_ptr_error(procId = tt_default_procid()) != TT_OK )
4824 myassert( 0 ); /* we should never get here */
4836 * The string generated for procId should never exceed the procId buf size.
4838 sprintf(procIdBuf,"%s_%d_%lu",
4839 _DtActNULL_GUARD(procId),
4840 (int) request->invocId,
4843 myassert( strlen(procIdBuf) < sizeof(procIdBuf) );
4847 procId = procIdBuf; /* no need to malloc */
4850 * Generate string of tmp file args for dtexec.
4853 if ( (invp = _DtActFindInvRec(request->invocId)) != NULL )
4856 _DtActFindChildRec(request->invocId,request->childId)) != NULL )
4862 for(i = 0; i < childp->numObjects; i++)
4865 if(!(IS_BUFFER_OBJ(invp->info[childp->argMap[i].argIdx].mask)))
4866 continue; /* not a buffer object */
4868 if ( !(p = invp->info[childp->argMap[i].argIdx].name) )
4869 continue; /* no tmp file name */
4871 /* Add up the string length of the file name */
4872 if((len += strlen(" -tmp ") + strlen(p)) < sizeof(tmpFileBuf))
4875 * Use the automatic tmpFileBuf if possible
4877 strcat(tmpFileBuf," -tmp ");
4878 strcat(tmpFileBuf,p);
4883 * Malloc more space if necessary
4886 tmpFiles = XtMalloc(len + 1);
4887 strcpy(tmpFiles,tmpFileBuf);
4888 strcpy(tmpFiles," -tmp ");
4893 if ( len > 0 && len < sizeof(tmpFileBuf) )
4894 tmpFiles = tmpFileBuf;
4898 myassert( 0 /* could not find child rec */ );
4905 /* Fill out the callback structure */
4906 data->actionLabel = XtNewString(request->clonedAction->label);
4907 data->associatedWidget = w;
4909 data->actionPtr = action;
4910 data->requestPtr = _DtCloneRequest(request);
4913 if ( _DtActionCommandInvoke(action->mask & _DtAct_WINTYPE_BITS, host, dir,
4914 action->u.cmd.execString.compiledMessage,
4915 action->u.cmd.termOpts.compiledMessage,
4916 request->currentHost,
4919 CmdInvSuccessfulRequest, (XtPointer)data,
4920 CmdInvFailedRequest, (XtPointer)data) )
4922 SET_INV_CMD_QUEUED(invp->state);
4925 if ( tmpFiles != tmpFileBuf )
4931 * Sets the 'currentHost' field within the request structure to
4932 * the name of the next exec host to try. Before any of the exec hosts
4933 * in the action definition are tried, we will try the host passed in as
4934 * part of the request, if one was specified.
4939 ActionRequest * request )
4942 ActionPtr action = request->clonedAction;
4944 int hostListSize = 0;
4945 char ** hostList = NULL;
4947 XtFree(request->currentHost);
4949 /* If this is the first call, we may need to parse the host list */
4950 if (action->u.cmd.execHostArray == NULL)
4952 /* Explicitly specified execHost overrides action definition execHost */
4953 if (request->execHost)
4954 ParseHostList(request->execHost, &hostList, &hostListSize, &hostCount);
4955 else if (action->u.cmd.execHosts.compiledMessage)
4957 ParseHostList(action->u.cmd.execHosts.compiledMessage, &hostList,
4958 &hostListSize, &hostCount);
4961 RemoveDuplicateHostNames(hostList, &hostCount);
4963 action->u.cmd.execHostArray = hostList;
4964 action->u.cmd.execHostCount = hostCount;
4967 if (action->u.cmd.execHostCount == 0)
4970 * Oh boy ... someone is trying to be nasty! The only way we could
4971 * have gotten here was to have the action's 'EXEC_HOST' field set
4972 * to nothing but a prompt string, and then the user left the string
4973 * empty! We'll default the local host, and hope it works.
4976 * fdt: we really should default to whatever has been configured
4977 * as the default execution host, followed by the LocalHost,
4978 * if they are not the same.
4980 request->currentHost = _DtGetLocalHostName();
4984 request->currentHost =
4985 XtNewString(action->u.cmd.execHostArray[request->hostIndex]);
4991 * This function takes a string of comma-separated host names, and adds them
4992 * to the passed-in string array.
4998 char *** hostListPtr,
4999 int * hostListSizePtr,
5000 int * hostCountPtr )
5005 _Xstrtokparams strtok_buf;
5007 workString = XtNewString(hostString);
5008 nextHost = _XStrtok(workString, ",", strtok_buf);
5012 nextHost = _DtStripSpaces(nextHost);
5014 if (strlen(nextHost) > 0)
5016 if (*hostCountPtr >= *hostListSizePtr)
5018 (*hostListSizePtr) += 5;
5019 (*hostListPtr) = (char **)XtRealloc((char *)(*hostListPtr),
5020 sizeof(char *) * (*hostListSizePtr));
5023 (*hostListPtr)[*hostCountPtr] = XtNewString(nextHost);
5027 nextHost = _XStrtok(NULL, ",", strtok_buf);
5035 * This function goes through the compiled list of exec hosts, and removes
5036 * any duplicate entries. It is not very useful to attempt to execute on
5037 * a given host, more than once.
5040 RemoveDuplicateHostNames (
5042 int * hostCountPtr )
5047 for (i = 0; i < *hostCountPtr; i++)
5049 for (j = i+1; j < *hostCountPtr; )
5051 if (strcmp(hostList[i], hostList[j]) == 0)
5053 /* Remove the second entry */
5054 XtFree(hostList[j]);
5055 for (k = j; k < (*hostCountPtr) - 1; k++)
5056 hostList[k] = hostList[k+1];
5067 * When one of the exec hosts fails, we add it to the list of failed
5068 * hostnames, so that if ultimately all of the hosts fail, we have a
5069 * list we can display within the error dialog.
5073 AddFailedHostToList (
5074 ActionRequest * request,
5080 if (request->badHostList)
5081 curLen = strlen(request->badHostList);
5085 request->badHostList = XtRealloc(request->badHostList,
5086 curLen + 10 + strlen(badHost));
5090 strcat(request->badHostList, ", ");
5091 strcat(request->badHostList, badHost);
5094 strcpy(request->badHostList, badHost);
5100 * This callback is invoked when the Command Invoker library has successfully
5101 * exectued an action. We need to free up everything associated with this
5106 CmdInvSuccessfulRequest(
5111 _DtActInvRecT *invp = NULL;
5112 _DtActChildRecT *childrecp = NULL;
5114 CallbackData *data = (CallbackData *) data2;
5117 * Mark this invocation step as done
5118 * The child process itself may not be done.
5120 if ((invp = _DtActFindInvRec(data->requestPtr->invocId)) != NULL )
5122 extern void *_DtCmdCheckQForId(DtActionInvocationID id);
5124 SET_INV_DONE(invp->state);
5125 RESET_INV_CMD_QUEUED(invp->state);
5127 * Are there still more commands queued for this request ?
5129 if ( _DtCmdCheckQForId(invp->id) )
5132 * If so; set the command queued bit
5134 SET_INV_CMD_QUEUED(invp->state);
5138 * This may not be the right place to set the child state for
5139 * command actions. The child process may already have communicated
5140 * its status via TT messaging OR it may already have exited.
5141 * For now we set the state here -- till we find a better place.
5143 if ((childrecp = _DtActFindChildRec(invp->id,data->requestPtr->childId)))
5144 childrecp->childState = _DtActCHILD_ALIVE_UNKNOWN;
5146 myassert(0 /* could not find child record */ );
5148 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
5151 myassert( 0 /* Couldn't find an invocation record */);
5153 _DtFreeRequest(data->requestPtr);
5154 XtFree(data->actionLabel);
5155 XtFree((char *)data);
5161 * This callback is invoked when the Command Invoker library has failed
5162 * to exectue an action. It there are additional execHosts to be processed,
5163 * then try the command again, using the next host. If there are no more
5164 * hosts, then post an error dialog, and give up (freeing all data
5165 * associated with this request).
5169 CmdInvFailedRequest(
5170 char *error_message,
5174 CallbackData * data = (CallbackData *) data2;
5175 String msg = error_message;
5177 ActionRequest * request;
5178 _DtActChildRecT *childp = NULL;
5179 _DtActInvRecT *invp = NULL;
5183 * If this was not the last host in the execHost list, then retry
5184 * the request, using the next host; if this was the last host,
5185 * then we failed, and it is time to post an error dialog. If the
5186 * host list had only one item, then to be backwards compatible,
5187 * we will display the message returned by the command invoker.
5188 * Otherwise, we will simple display the list of execHosts, along
5189 * with a message saying they could not be accessed.
5191 request = data->requestPtr;
5193 if (request->clonedAction)
5194 action = request->clonedAction;
5196 action = data->actionPtr;
5197 request->hostIndex++;
5198 AddFailedHostToList(request, request->currentHost);
5200 if ( !(invp = _DtActFindInvRec(request->invocId) ) )
5201 myassert( 0 /* could not find invocation record */ );
5203 if ( !(childp=_DtActFindChildRec(request->invocId,request->childId)))
5204 myassert( 0 /* could not find child record */ );
5207 * Make sure the CMD_QUEUED bit is set correctly
5211 extern void *_DtCmdCheckQForId(DtActionInvocationID id);
5213 SET_INV_DONE(invp->state);
5214 RESET_INV_CMD_QUEUED(invp->state);
5216 * Are there still more commands queued for this request ?
5218 if ( _DtCmdCheckQForId(invp->id) )
5221 * If so; set the command queued bit
5223 SET_INV_CMD_QUEUED(invp->state);
5227 if (request->hostIndex < action->u.cmd.execHostCount)
5230 * Free up the child structure for the failed command request
5231 * We may be trying again on another host but a new child rec
5232 * will be allocated in PrepareAndExecute().
5235 if ( invp && childp )
5238 * Delete child record for failed exec on this host
5240 _DtActDeleteChildRec(invp,childp);
5241 SET_INV_PENDING(invp->state);
5242 if ( ! invp->numChildren )
5243 RESET_INV_WORKING(invp->state);
5246 /* Retry, using the next host */
5247 PrepareAndExecuteAction(data->associatedWidget, request);
5252 if ( invp && childp )
5256 * How can we tell if the Invocation COMPLETE bit
5257 * needs to be set here?
5258 * How about if no invocation is pending or working?
5260 SET_INV_ERROR(invp->state);
5261 childp->childState = _DtActCHILD_FAILED;
5265 /* No more hosts (they all failed); put up error dialog */
5266 if (action->u.cmd.execHostCount <= 1)
5268 /* Be backwards compatible */
5269 CommandInvokerError(data->associatedWidget,
5271 msg + data->offset);
5275 MultiHostAccessError(data->associatedWidget, request->clonedAction->label,
5276 request->badHostList);
5281 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
5282 _DtFreeRequest(request);
5283 XtFree(data->actionLabel);
5285 XtFree((char *)data);
5291 * This function maps a filename relative to 'host' to be relative to
5292 * 'newHost'. If newHost is NULL, then the local host is assumed.
5294 * The returned string must be freed by the caller.
5299 const char * curHost,
5302 const char * newHost )
5304 char buf[MAXPATHLEN];
5307 char *netpath = NULL;
5311 * Create the full path name relative to curHost
5314 buf[0]='\0'; /* empty string to start with */
5320 /* check if there is already a '/' separator */
5323 DtLastChar(buf,&chp,&clen);
5324 if ( !( (clen == 1) && (*chp == '/')) )
5330 /* We should have constructed a file name string now */
5331 myassert(buf[0] != '\0');
5335 if ( _DtIsSameHost(curHost,newHost) )
5338 * The current host is the same as the new host
5339 * so no file name translation is necessary
5341 return XtNewString(buf);
5344 * The current host is not the same as the new host -- find the
5345 * cannonical netfile name then reinterpret it on the new host.
5347 switch ( tt_ptr_error(netpath = tt_host_file_netfile(curHost,buf)) )
5354 case TT_ERR_DBAVAIL:
5357 case TT_ERR_DBEXIST:
5360 case TT_ERR_INTERNAL:
5369 switch ( tt_ptr_error(path = tt_host_netfile_file(newHost,netpath)) )
5376 case TT_ERR_DBAVAIL:
5379 case TT_ERR_DBEXIST:
5382 case TT_ERR_INTERNAL:
5393 * Convert the file path which is relative to curHost to be
5394 * relative to the local host.
5396 if ( _DtIsSameHost(curHost,NULL) )
5399 * The current host is the same as the local host
5400 * so no file name translation is necessary
5402 return XtNewString(buf);
5405 * The current host is not the same as the local host -- find the
5406 * cannonical netfile name then reinterpret it on the local host.
5408 switch ( tt_ptr_error(netpath = tt_host_file_netfile(curHost,buf)) )
5415 case TT_ERR_DBAVAIL:
5418 case TT_ERR_DBEXIST:
5421 case TT_ERR_INTERNAL:
5430 switch ( tt_ptr_error(path = tt_netfile_file(netpath)) )
5437 case TT_ERR_DBAVAIL:
5440 case TT_ERR_DBEXIST:
5443 case TT_ERR_INTERNAL:
5454 * Free up the memory allocated by tooltalk filenaming code here so
5455 * downstream code need not worry about it.
5462 path = XtNewString(s);