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 extern int _DtActDeleteChildRec( _DtActInvRecT *invp, _DtActChildRecT *childp);
112 extern int _DtActionCommandInvoke(
121 void (*success_proc)(),
123 void (*failure_proc)(),
128 #define _MAX_MAP_ATTEMPTS 100 /* Maximum nuber of "MAPS" that will
130 #define _DT_ACTION_MAX_CLOSE_TRIES 5
132 /******** Public Function Declarations ********/
134 void _DtCreateErrorDialog(
138 Boolean _DtCompileMessagePiece(
140 ActionRequest *request,
145 unsigned long processingMask,
146 Boolean ** paramUsed,
147 int * promptDataIndex ) ;
148 ActionRequest * _DtCloneRequest (
149 ActionRequest * request) ;
151 ActionRequest *request) ;
153 char * _DtFindCwd( void ) ;
155 char * _DtActMapFileName(
156 const char * curHost,
159 const char * newHost ) ;
161 extern void _DtProcessTtRequest(
163 ActionRequest *request,
165 char * relPathDir ) ;
166 extern Tt_status _DtInitializeToolTalk(Widget w);
168 extern Boolean _DtEmptyString(
171 /******** End Public Function Declarations ********/
174 /******** Static Function Declarations ********/
177 static void FreeErrorDialog(
180 XtPointer call_data) ;
181 static void InvalidFilename(
185 static void HostAccessError(
189 static void MultiHostAccessError(
193 static void NoActionError(
195 DtShmBoson origNameQuark,
201 static void MapError(
203 char * actionName ) ;
204 static void CommandInvokerError(
207 char * errorString) ;
208 static void NoToolTalkConnectionError(
212 static void TmpFileCreateError(
216 static void TmpFileOpenError(
220 static void TmpFileWriteError(
224 static void UnSupportedObject(
228 static void SetExecHost(
229 ActionRequest * request) ;
230 static void ParseHostList (
232 char *** hostListPtr,
233 int * hostListSizePtr,
234 int * hostCountPtr) ;
235 static void RemoveDuplicateHostNames (
237 int * hostCountPtr ) ;
238 static void AddFailedHostToList (
239 ActionRequest * request,
241 static int _DtAddEntry(
245 static void TryToTypeFile(
250 char ** resolvedPath);
251 static ActionRequest * CreateActionRequest(
260 _DtActInvRecT *invp);
261 static _DtActInvRecT *CreateInvocationRecord(
266 static Boolean ParseFileArgument(
268 ActionRequest * request,
269 ObjectData * objectData,
274 static void AddPrompt(
278 PromptEntry **prompts) ;
279 static int MatchParamsToAction(
280 ActionRequest *request,
282 PromptEntry **prompts) ;
283 static void ProcessOneSegment(
284 ActionRequest * request,
286 PromptEntry **prompts,
288 Boolean * argsOptionFound,
289 int * lastArgReferenced,
291 Boolean * paramUsed) ;
292 static ActionPtr CloneActionDBEntry(
294 static void CloneParsedMessage(
295 parsedMsg * old_pmsg,
296 parsedMsg * new_pmsg ) ;
297 static void FreeParsedMessage(
298 parsedMsg * parsedMessage) ;
299 static parsedMsg * CloneParsedMessageArray(
300 parsedMsg * pmsgArray,
302 static void FreeParsedMessageArray(
303 parsedMsg * parsedMessageArray,
305 static Boolean InsertArgumentString(
309 ActionRequest *request,
314 Boolean addLeadingSpace,
315 unsigned long processingMask ) ;
316 static void InsertUnmappedArgumentString(
320 Boolean addLeadingSpace ) ;
321 static char * GrowMsgBuffer(
325 static void CmdInvSuccessfulRequest(
328 static void CmdInvFailedRequest(
331 static void InitiateDtRequest(
333 ActionRequest *request) ;
334 static Boolean ResolveDtNotifyMessagePieces(
336 ActionRequest *request,
338 char * relPathDir ) ;
339 static void InitiateDtNotifyMessage(
341 ActionRequest *request ) ;
342 static void PrepareAndExecuteAction(
344 ActionRequest *request);
345 static void __ExtractCWD(
346 ActionRequest *request,
349 Boolean useObjectInfo) ;
350 static void ContinueRequest(
353 XtPointer call_data) ;
354 static void CancelRequest(
357 XtPointer call_data) ;
358 static void CreateContinueDialog(
360 ActionRequest *request,
362 PromptEntry *prompts) ;
363 static void CancelPromptDialog(
365 PromptDialog *dialog,
366 XtPointer call_data) ;
367 static void ProcessPromptDialog(
369 PromptDialog *dialog,
370 XtPointer call_data) ;
371 static void ChangePromptTraversal(
373 PromptDialog *dialog,
374 XtPointer call_data) ;
375 static void CreatePromptDialog(
377 ActionRequest *request,
379 PromptEntry *prompts) ;
380 static Boolean MoreArgumentsToProcess(
381 ActionRequest *request) ;
382 static Boolean ProcessRequest(
384 ActionRequest *request) ;
385 static void InitLocalizedStrings( void ) ;
386 static int LinkToTypeQuark(
390 char **resolvedPath) ;
391 static void CancelOut(
395 XtPointer num_params);
396 static void InitiateCommandInvokerRequest(
398 ActionRequest *request,
401 static void ProcessCommandInvokerRequest(
403 ActionRequest *request,
406 static Boolean ResolveCommandInvokerMessagePieces(
408 ActionRequest *request,
411 static Tt_callback_action _DbReloadCB(Tt_message m, Tt_pattern p);
412 static void _DtActTimerCB( XtPointer clientData,
413 XtIntervalId timerId);
414 static void _DtActIndicatorCB( XtPointer clientData,
415 XtIntervalId timerId);
417 /******** End Static Function Declarations ********/
420 /* Pointers to localizable strings */
421 static String PromptDialogTitle;
422 static String ErrorPostfix;
423 static String PromptDialogLabel;
424 static String ContinueMessage;
425 static String HostErrorMsg;
426 static String HostErrorMsg2;
427 static String NoActionMsg;
428 static String NoActionMsg2;
429 static String NoActionMsg3;
430 static String MapErrorMsg;
431 static String InvalidFileMsg;
432 static String MultiHostErrorMsg;
433 static String IcccmReqErrorMsg;
434 static String NoToolTalkConnMsg;
435 static String UnSupportedObjMsg;
436 static String TmpFileCreateErrorMsg;
437 static String TmpFileOpenErrorMsg;
438 static String TmpFileWriteErrorMsg;
442 * These error messages are used in the ActionTt.c file
443 * but were declared static to this file. -- For the
444 * time being I made them global to get things to work.
446 String ToolTalkErrorMsg;
447 String ToolTalkErrorMsg2;
448 String TtFileArgMapErr;
452 * Variables needed to make the "Escape" key remove the prompt dialog.
454 static XtActionsRec actionTable [] = {
455 {"Escape", (XtActionProc) CancelOut},
457 static char translations_escape[] = "<Key>osfCancel:Escape()";
461 #define PROMPT_HELP "vg_act"
464 /* Maximum Indicator activation duration (in milliseconds) */
466 #define INDICATOR_TIME (120 * 1000)
467 #define MIN_INDICATOR_TIME (5 * 1000)
471 /******************************************************************************
472 ******************************************************************************
474 * Public API Functions
476 ******************************************************************************
477 *****************************************************************************/
479 /*******************************************************************************
480 * DtActionInvoke -- invoke an action
481 * Widget w; ( widget for UI needs)
482 * char *action; ( action name )
483 * int aac; ( action arg count )
484 * ActionArgp aap; ( action argument pointer )
485 * char *termOpts; ( (opt) terminal options)
486 * char *execHost; ( (opt) execution host )
487 * char *cwd; ( (opt) cwd for this action )
488 * int useIndicator; ( 1 ==> use indicator, 0 ==> not )
489 * DtActionCallbackProc statusUpdateCb; (user supplied fcn)
490 * XtPointer client_data (user supplied client data)
491 *****************************************************************************/
502 DtActionCallbackProc statusUpdateCb,
503 XtPointer client_data)
506 ActionRequest *request;
507 char *contextHost= NULL;/* dummy to replace old parameter */
508 _DtActInvRecT *invp; /* pointer to invocation record */
509 Tt_status status = TT_OK;
510 static Boolean initialized = False;
511 extern XtAppContext *_DtInitAppContextp;
512 _DtSvcWidgetToAppContext(w);
515 _DtSvcAppLock(*_DtInitAppContextp);
517 /* We can't handle gadgets; use the parent, if necessary */
527 InitLocalizedStrings();
530 * Make sure Tooltalk is initialized
532 status = _DtInitializeToolTalk(w);
535 NoToolTalkConnectionError(w, action, status);
536 _DtSvcProcessUnlock();
541 * Create the DtTmp directory, if necessary.
543 tmpDir = _DtGetDtTmpDir();
545 mode = (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH );
546 if ( mkdir(tmpDir,mode) )
549 * Tmp directory creation failure.
551 * Make one attempt to create the parent directory if the error
552 * was because of a missing path component -- It may be because
553 * the "$HOME/.dt" directory hasn't yet been created.
555 if ( errno == ENOENT )
557 char *parentDir = _DtDirname(tmpDir);
558 if (parentDir && 0 == mkdir(parentDir,mode))
560 if (parentDir) XtFree(parentDir);
565 _DtInitializeCommandInvoker( XtDisplay(w),
566 NULL, /* bms tool class -- ignored */
567 NULL, /* application class -- ignored */
568 (DtSvcMsgContext)NULL, /* reloadDBHandler -- none here */
569 (_DtInitAppContextp ?
570 *_DtInitAppContextp : XtWidgetToApplicationContext(w)));
573 _DtSvcProcessUnlock();
575 /* Start the activity indicator */
576 if ( useIndicator ) {
577 _DtSendActivityNotification(INDICATOR_TIME);
580 if ( (invp = CreateInvocationRecord(action,w,aap,aac)) == NULL)
582 myassert( 0 ); /* no request structure --should never happen */
583 /* give up -- cannot allocate record */
585 if ( useIndicator ) _DtSendActivityDoneNotification();
586 _DtSvcAppUnlock(*_DtInitAppContextp);
587 _DtSvcAppUnlock(app);
593 if ( useIndicator ) {
594 /* Start timer for minimum blink time */
595 XtAppAddTimeOut(XtWidgetToApplicationContext(w),
597 (XtTimerCallbackProc) _DtActIndicatorCB,
598 (XtPointer) invp->id );
602 * Add user callback info to the new invocation record.
604 invp->client_data = client_data;
605 invp->cb = statusUpdateCb;
607 /* Create and fill in the request structure */
608 if ( !IS_INV_FINISHED(invp->state) && (request = CreateActionRequest (
609 w,action,aap,aac,termOpts,execHost,contextHost,cwd,invp)) != NULL)
611 if (ProcessRequest(w, request))
613 /* all done invoking ? */
614 RESET_INV_PENDING(invp->state);
616 /* We should only get here if all requests have been honored */
617 SET_INV_COMPLETE(invp->state);
620 * Evaluate whether we are done with this invocation.
621 * We may have to return values to the caller.
623 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
624 _DtFreeRequest(request);
626 /* Otherwise, a dialog was posted; request will be freed later */
630 * Set the indicator that the invocation Id has been returned
631 * add a timer so that we can return status info once the
632 * caller has gotten the invocation Id.
634 SET_INV_ID_RETURNED(invp->state);
635 XtAppAddTimeOut(XtWidgetToApplicationContext(w),
636 0 /* call back immediately */,
637 (XtTimerCallbackProc) _DtActTimerCB,
638 (XtPointer) invp->id );
640 _DtSvcAppUnlock(*_DtInitAppContextp);
641 _DtSvcAppUnlock(app);
647 DtDbReloadNotify( DtDbReloadCallbackProc proc, XtPointer client_data)
652 extern XtAppContext *_DtInitAppContextp;
655 if (NULL == proc) return;
657 _DtSvcAppLock(*_DtInitAppContextp);
660 * Check if we need to initialize tooltalk
662 status = _DtInitializeToolTalk(NULL);
663 if (TT_OK != status) {
664 _DtSvcAppUnlock(*_DtInitAppContextp);
669 * This function register a ToolTalk pattern for every
672 pattern = tt_pattern_create();
673 if (tt_ptr_error(pattern) != TT_OK) {
674 _DtSvcAppUnlock(*_DtInitAppContextp);
678 if (tt_pattern_scope_add(pattern, TT_SESSION) != TT_OK) {
679 _DtSvcAppUnlock(*_DtInitAppContextp);
682 if (tt_pattern_category_set(pattern, TT_OBSERVE) != TT_OK) {
683 _DtSvcAppUnlock(*_DtInitAppContextp);
686 if (tt_pattern_class_add(pattern, TT_NOTICE) != TT_OK) {
687 _DtSvcAppUnlock(*_DtInitAppContextp);
690 if (tt_pattern_state_add(pattern, TT_SENT) != TT_OK) {
691 _DtSvcAppUnlock(*_DtInitAppContextp);
694 sessId = tt_default_session();
695 if (tt_pattern_session_add(pattern, sessId) != TT_OK) {
696 _DtSvcAppUnlock(*_DtInitAppContextp);
700 if (tt_pattern_op_add(pattern, "DtTypes_Reloaded") != TT_OK) {
701 _DtSvcAppUnlock(*_DtInitAppContextp);
706 * Store information needed by the callback in the user data
707 * fields of the pattern.
709 status = tt_pattern_user_set(pattern, 0, (void *)proc);
710 if (status != TT_OK) {
711 _DtSvcAppUnlock(*_DtInitAppContextp);
714 status = tt_pattern_user_set(pattern, 1, (void *)client_data);
715 if (status != TT_OK) {
716 _DtSvcAppUnlock(*_DtInitAppContextp);
721 * _DbReloadCB is the ToolTalk callback which will call
724 if (tt_pattern_callback_add(pattern, _DbReloadCB) != TT_OK) {
725 _DtSvcAppUnlock(*_DtInitAppContextp);
729 if (tt_pattern_register(pattern) != TT_OK) {
730 _DtSvcAppUnlock(*_DtInitAppContextp);
734 _DtSvcAppUnlock(*_DtInitAppContextp);
738 /******************************************************************************
739 ******************************************************************************
741 * Private API Functions
743 ******************************************************************************
744 *****************************************************************************/
747 _DtActTimerCB( XtPointer clientData, XtIntervalId IntId)
749 _DtActExecutionLeafNodeCleanup((unsigned long)clientData,
754 _DtActIndicatorCB( XtPointer clientData, XtIntervalId IntId )
756 unsigned long invocId = (unsigned long) clientData;
757 _DtActInvRecT *invRecP = _DtActFindInvRec( invocId );
759 if ( !invRecP || IS_INV_FINISHED(invRecP->state) )
761 /* Turn off the activity indicator */
762 _DtSendActivityDoneNotification();
767 * Let the action turn off the indicator when invocation
770 SET_INV_INDICATOR_ON(invRecP->state);
774 /***************************************************************************
776 * Routines and static data to support DtDbReloadNotify which supplies
777 * the user with transparent access to the messaging system for
778 * notification of action/datatypes database changes.
780 ****************************************************************************/
784 * A ToolTalk callback function used to map callback arguments to
785 * the callback function specified by the user. This function invokes the
786 * user-defined DtReloadNotifyProc callback with the desired client_data.
789 static Tt_callback_action
790 _DbReloadCB(Tt_message m, Tt_pattern p)
792 DtDbReloadCallbackProc proc;
793 XtPointer client_data;
796 * user data 0: DtDbReloadCallbackProc proc;
797 * user data 1: XtPointer client_data;
799 proc = (DtDbReloadCallbackProc)tt_pattern_user(p, 0);
800 client_data = (XtPointer)tt_pattern_user(p, 1);
803 * Call registered callback function.
805 if (proc) (*proc)(client_data);
807 return TT_CALLBACK_PROCESSED;
811 /***************************************************************************/
812 /***************************************************************************/
813 /* Error Dialog Code */
814 /***************************************************************************/
815 /***************************************************************************/
818 * 'Ok' callback for the generic error dialogs. It will simply destroy
826 XtPointer call_data )
829 XtDestroyWidget(XtParent(w));
834 * Generic function used to create an error dialog.
838 _DtCreateErrorDialog(
849 XWindowAttributes xwa;
851 Boolean is_mapped = False;
854 fmt = XtNewString((char *)Dt11GETMESSAGE(2, 1, "%1$s%2$s%3$s"));
856 /* Create the title string for the dialog */
857 title = (char *)XtMalloc((Cardinal)(strlen(PromptDialogTitle) +
859 strlen(ErrorPostfix) +
862 (void)sprintf(title, fmt, PromptDialogTitle, actionName, ErrorPostfix);
866 ok = XmStringCreateLocalized((String)_DtOkString);
870 status = XGetWindowAttributes (XtDisplay (w), XtWindow (w), &xwa);
871 if (status && (xwa.map_state == IsViewable))
875 /* Create the error dialog */
877 XtSetArg(args[n], XmNmessageString, msg); n++;
878 XtSetArg(args[n], XmNtitle, title); n++;
879 XtSetArg(args[n], XmNokLabelString, ok); n++;
880 XtSetArg(args[n], XmNuseAsyncGeometry, True); n++;
883 XtSetArg (args[n], XmNdefaultPosition, False);
886 dialog = XmCreateErrorDialog(w, "errorDialog", args, n);
890 /* Set up callbacks */
891 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
892 XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
893 XtAddCallback(dialog, XmNokCallback, FreeErrorDialog,
897 * If the widget is not mapped, center this dialog.
901 Dimension dialogWd, dialogHt;
902 Widget dialogShell = XtParent(dialog);
904 XtSetArg(args[0], XmNmappedWhenManaged, False);
905 XtSetValues(dialogShell, args, 1);
907 XtManageChild(dialog);
908 XtRealizeWidget (dialogShell);
910 XtSetArg(args[0], XmNwidth, &dialogWd);
911 XtSetArg(args[1], XmNheight, &dialogHt);
912 XtGetValues(dialog, args, 2);
914 XtSetArg (args[0], XmNx,
915 (WidthOfScreen(XtScreen(dialog)) - dialogWd) / 2U);
916 XtSetArg (args[1], XmNy,
917 (HeightOfScreen(XtScreen(dialog)) - dialogHt) / 2U);
918 XtSetValues (dialog, args, 2);
920 XtSetArg(args[0], XmNmappedWhenManaged, True);
921 XtSetValues(dialogShell, args, 1);
924 /* Display the dialog */
925 XtManageChild(dialog);
930 * Error handler for when the user supplied a file name which cannot
931 * be accessed. Displays an error dialog. Most often, this is caused
932 * when a filename with an embedded space is received in the object list.
942 XmString pt1, pt2, msg;
944 /* Construct the error message */
945 pt1 = XmStringCreateLocalized(InvalidFileMsg);
946 pt2 = XmStringCreateLocalized(filename);
947 msg = XmStringConcat(pt1, pt2);
949 _DtCreateErrorDialog(w, actionName, msg);
957 * Error handler for when the user supplied a host name which cannot
958 * be accessed. Displays an error dialog.
968 XmString pt1, pt2, pt3, msg, msg2;
970 /* Construct the error message */
971 pt1 = XmStringCreateLocalized(HostErrorMsg);
972 pt2 = XmStringCreateLocalized(hostName);
973 pt3 = XmStringCreateLocalized(HostErrorMsg2);
975 msg = XmStringConcat(pt1, pt2);
976 msg2 = XmStringConcat(msg, pt3);
978 _DtCreateErrorDialog(w, actionName, msg2);
989 * Error handler for when the user supplied a collection of host names
990 * which cannot be accessed. Displays an error dialog.
994 MultiHostAccessError(
1001 char * buf = XtMalloc(strlen(MultiHostErrorMsg) + strlen(hostList) + 10);
1003 sprintf(buf, MultiHostErrorMsg, hostList);
1004 msg = XmStringCreateLocalized(buf);
1005 _DtCreateErrorDialog(w, actionName, msg);
1011 /***************************************************************************
1013 * MapError - this function creates an error message when an action
1014 * cannot be executed because of too many "MAPs".
1018 * Widget w; - Widget needed for posting the error dialog.
1020 * String actionName; - The name of the action.
1024 ****************************************************************************/
1032 XmString msg = XmStringCreateLocalized(MapErrorMsg);
1033 _DtCreateErrorDialog(w, actionName, msg);
1039 * Error handler for when an action definition cannot be found to
1040 * match an object of a particular type. Displays an error dialog.
1041 * A different message is displayed when no objects is supplied.
1046 * Since we use tmp files for buffers and do not support
1047 * strings; we need not add special code for buffer and
1050 /* fdt: Will need to also handle a string or a buffer ... eventually */
1055 DtShmBoson origNameQuark,
1063 char *msgbuf = XtMalloc(2*MAXPATHLEN);
1067 /* Construct the error message */
1068 if ((host == NULL) && (dir == NULL) && (file == NULL) && (type == NULL) )
1070 (void)sprintf(msgbuf,NoActionMsg2,actionName);
1072 else if ( (type != NULL ) && (file == NULL) && (dir == NULL))
1075 * We are dealing with a buffer object for which an action couldn't
1078 (void)sprintf(msgbuf,NoActionMsg3,actionName,type);
1082 name = (char *)XtMalloc((Cardinal)((host ? strlen(host) : 0) +
1083 (dir ? strlen(dir) : 0) +
1084 (file ? strlen(file) : 0) + 10));
1087 /* Construct the file name */
1090 (void)strcat(name, host);
1091 (void)strcat(name, ":");
1096 (void)strcat(name, dir);
1097 if (strcmp(dir, "/") != 0)
1098 (void)strcat(name, "/");
1102 (void)strcat(name, file);
1104 (void)sprintf(msgbuf,NoActionMsg,actionName,name,type);
1107 msg = XmStringCreateLocalized(msgbuf);
1109 _DtCreateErrorDialog(w, actionName, msg);
1117 * Error handler for when the Command Invoker detects an error, and
1118 * send us a failure response to our action request.
1119 * Display an error dialog.
1123 CommandInvokerError(
1126 String errorString )
1131 msg = XmStringCreateLocalized(errorString);
1132 _DtCreateErrorDialog(w, actionName, msg);
1138 * If an action requires a ToolTalk connection, and we were unable to get
1139 * one, then we will fail and post an error dialog.
1143 NoToolTalkConnectionError(
1150 char *errmsg, *statmsg;
1152 if (TT_OK == status)
1155 statmsg = tt_status_message(status);
1156 errmsg = XtMalloc(strlen(NoToolTalkConnMsg) + strlen(statmsg) + 2);
1157 sprintf(errmsg, NoToolTalkConnMsg, statmsg);
1159 msg = XmStringCreateLocalized(errmsg);
1160 _DtCreateErrorDialog(w, actionName, msg);
1167 TmpFileCreateError( Widget w, char *actionName, char *dirName)
1170 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1172 sprintf(msgbuf,TmpFileCreateErrorMsg,_DtActNULL_GUARD(dirName),
1175 msg = XmStringCreateLocalized(msgbuf);
1176 _DtCreateErrorDialog(w, actionName, msg);
1182 TmpFileOpenError( Widget w, char *actionName, char *fileName)
1185 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1187 sprintf(msgbuf,TmpFileOpenErrorMsg,_DtActNULL_GUARD(fileName),
1190 msg = XmStringCreateLocalized(msgbuf);
1191 _DtCreateErrorDialog(w, actionName, msg);
1197 TmpFileWriteError( Widget w, char *actionName, char *fileName)
1200 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1202 sprintf(msgbuf,TmpFileWriteErrorMsg,_DtActNULL_GUARD(fileName),
1205 msg = XmStringCreateLocalized(msgbuf);
1206 _DtCreateErrorDialog(w, actionName, msg);
1212 UnSupportedObject( Widget w, char *actionName, int objClass)
1215 char *msgbuf = XtMalloc(_DtAct_MAX_BUF_SIZE);
1217 sprintf(msgbuf,UnSupportedObjMsg,objClass,actionName);
1219 msg = XmStringCreateLocalized(msgbuf);
1220 _DtCreateErrorDialog(w, actionName, msg);
1228 /***************************************************************************/
1229 /***************************************************************************/
1230 /* Main Work Functions For _DtActionInvoke() */
1231 /***************************************************************************/
1232 /***************************************************************************/
1236 * Load the globals pointing to any localizable strings.
1240 InitLocalizedStrings( void )
1243 PromptDialogTitle = XtNewString(((char *)Dt11GETMESSAGE(2, 3, "Action: ")));
1244 ErrorPostfix = XtNewString(((char *)Dt11GETMESSAGE(2, 4, " [Error]")));
1245 PromptDialogLabel = XtNewString(
1246 ((char *)Dt11GETMESSAGE(2, 5, "Please enter the following information:")));
1247 ContinueMessage = XtNewString(
1248 ((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.")));
1249 HostErrorMsg =XtNewString(((char *)Dt11GETMESSAGE(2, 7, "The following host was not accessible:\n\n ")));
1252 HostErrorMsg2 =XtNewString(((char *)Dt11GETMESSAGE(2, 8, "\n\nThis may be because the remote host's file\nsystem is not properly mounted.\n\n")));
1254 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")));
1256 NoActionMsg =XtNewString(
1257 ((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")));
1258 NoActionMsg2 = XtNewString(((char *)Dt11GETMESSAGE(2, 10, "Action \"%s\" was not found.\n")));
1259 InvalidFileMsg = XtNewString(((char *)Dt11GETMESSAGE(2, 11, "The following file was not found:\n\n ")));
1260 MapErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2, 12, "This action cannot be executed because it contains too\nmany levels of MAPs, or the mapping is \"circular\".")));
1261 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?")));
1262 IcccmReqErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2,14,"The request to service this action has failed")));
1263 NoToolTalkConnMsg = XtNewString(((char *)Dt11GETMESSAGE(2,15,"The request to service this action has failed.\nA ToolTalk connection could not be established:\n\n%s")));
1264 ToolTalkErrorMsg = XtNewString(((char *)Dt11GETMESSAGE(2,16, "The request to service this action has failed")));
1265 ToolTalkErrorMsg2 = XtNewString(((char *)Dt11GETMESSAGE(2,17, "The request to service this action has failed for the following reason:\n\n %s")));
1266 TtFileArgMapErr = XtNewString((char *)Dt11GETMESSAGE(2,18,"An error occurred while attempting to map one of\nthe file arguments."));
1267 NoActionMsg3 =XtNewString(
1268 ((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")));
1269 UnSupportedObjMsg = XtNewString(
1270 ((char *)Dt11GETMESSAGE(2, 21, "Unsupported input object class: \"%d\"\nfor action: \"%s\".")));
1271 TmpFileCreateErrorMsg = XtNewString(
1272 ((char *)Dt11GETMESSAGE(2, 22, "Unable to create a temporary file in directory: \"%s\"\nfor the action named: \"%s\"")));
1273 TmpFileOpenErrorMsg = XtNewString(
1274 ((char *)Dt11GETMESSAGE(2, 23, "Unable to open a temporary file: \"%s\"\nfor the action named: \"%s\"")));
1275 TmpFileWriteErrorMsg = XtNewString(
1276 ((char *)Dt11GETMESSAGE(2, 24, "Unable to write a temporary file: \"%s\"\nfor the action named: \"%s\"")));
1280 * This function takes the information supplied by the caller of
1281 * _DtActionInvoke(), and turns it into an internal format. This
1282 * includes parsing out each of the file names, and converting the
1283 * type from a string to an integer.
1285 * The structure returned must be freed up eventually.
1288 static ActionRequest *
1289 CreateActionRequest(
1298 _DtActInvRecT *invp )
1303 ObjectData * objectDataArray;
1304 ObjectData objectData;
1305 ActionRequest * request;
1308 /* Allocate a new request structure -- zero filled */
1309 request = (ActionRequest *) XtCalloc(1,(Cardinal)sizeof(ActionRequest));
1311 request->actionName = XtNewString(actionName);
1314 request->termOpts = XtNewString(termOpts);
1317 request->execHost = XtNewString(execHost);
1320 request->cwdHost = XtNewString(cwdHost);
1323 request->cwdDir = XtNewString(cwdDir);
1325 request->objsUsed = -1; /* -1 => not yet determined */
1328 request->invocId = invp->id;
1330 /* If there are no objects, then there's no reason to continue */
1331 if ((aac <= 0) || (aap == NULL))
1335 * Allocate space for all the object data at once
1337 objectDataArray = (ObjectData *) XtCalloc(aac,(sizeof(ObjectData)));
1340 * process object names -- assume all file names are of the form
1341 * /path/file (do NOT allow host:/path/file)
1343 for ( i = 0; i < aac ; i++ )
1345 memset((void *)&objectData,0,sizeof(ObjectData));
1346 if ( (aap+i)->argClass == DtACTION_FILE )
1348 if (ParseFileArgument(w, request, &objectData, NULL ,
1349 aap[i].u.file.name, NULL , True))
1351 XtFree((char *)objectDataArray);
1355 else if ( (aap+i)->argClass == DtACTION_BUFFER )
1358 * Check if we've already created a tmp file for this buffer
1359 * if so fill in the request structure as if this were a file
1362 if ( invp->info[i].name )
1365 * Use the tmp file name and type stored in the invocation rec.
1366 * The FILE bit will be set in the object mask -- we will also
1367 * set the BUFFER bit to indicate that this is a tmp file
1368 * representing a buffer.
1370 if (ParseFileArgument(w, request, &objectData, NULL ,
1371 invp->info[i].name, invp->info[i].type , True))
1373 XtFree((char *)objectDataArray);
1377 * Set the buffer object bit as well -- and check whether
1378 * this buffer is intended to be writable, if not reset the
1379 * writable bit set in ParseFileArgument().
1381 SET_BUFFER_OBJ(objectData.mask);
1382 if ( !(aap[i].u.buffer.writable) )
1383 RESET_WRITE_OBJ(objectData.mask);
1385 * Save the buffer type info if we have it
1387 if ( aap[i].u.buffer.type )
1388 objectData.type = _DtDtsMMStringToBoson(aap[i].u.buffer.type);
1391 * Save the original buffer pointer and size for this
1394 if ( aap[i].u.buffer.bp )
1396 objectData.u.file.bp = aap[i].u.buffer.bp;
1397 objectData.u.file.sizebp = aap[i].u.buffer.size;
1403 /* Do buffer stuff here */
1404 SET_BUFFER_OBJ(objectData.mask);
1405 if ( aap[i].u.buffer.writable )
1406 SET_WRITE_OBJ(objectData.mask);
1409 * If the buffer type has been passed in to us save its quark
1410 * in the object structure now. When/if the type is determined
1411 * later this object record should be filled in with the
1414 if ( aap[i].u.buffer.type )
1415 objectData.type = _DtDtsMMStringToBoson(aap[i].u.buffer.type);
1419 * We have already determined the buffer type when creating
1420 * the invocation record. So get the type string
1421 * from the invocation record.
1423 myassert(invp->info[i].type);
1424 if (invp->info[i].type)
1426 objectData.type = _DtDtsMMStringToBoson(invp->info[i].type);
1431 * Save buffer contents
1433 if ( aap[i].u.buffer.bp )
1435 objectData.u.buffer.size = aap[i].u.buffer.size;
1436 objectData.u.buffer.bp = aap[i].u.buffer.bp;
1439 myassert(0 /* null buffer pointer */ );
1440 objectData.u.buffer.bp = NULL;
1441 objectData.u.buffer.size = 0;
1446 /* structure assignment */
1447 objectDataArray[i] = objectData;
1451 request->numObjects = numObjects;
1452 request->objects = objectDataArray;
1458 /******************************************************************************
1460 * static _DtActInvRecT *
1461 * CreateInvocationRecord(actionName,w,aap,aac)
1462 * Create an invocation record and fill in argument information
1463 * return a pointer to the newly allocated invocation record.
1465 *****************************************************************************/
1466 static _DtActInvRecT *
1467 CreateInvocationRecord(
1474 _DtActInvRecT *invp; /* pointer to invocation record */
1478 * allocate invocation record and ID to return to caller
1480 invp = _DtActAllocInvRec();
1484 * RWV --> ideally we would need error message here
1485 * but if we are unable to allocate the record we
1486 * would in all likelyhood be unable to allocate the
1487 * error message as well. We will essentially assume
1488 * allocation does not fail as is done throughout the
1493 myassert(invp->id != 0);
1495 SET_INV_PENDING(invp->state);
1497 invp->numChildren = 0;
1498 invp->childRec = NULL;
1501 * Fill in argument information
1511 invp->info = (_DtActArgInfo *)XtCalloc(aac,sizeof(_DtActArgInfo));
1513 for ( i=0; i < aac; i++ )
1515 if ( aap[i].argClass == DtACTION_BUFFER )
1517 int fd; /* tmp file descriptor */
1518 char *format; /* name template (printf format) */
1519 char *is_executable; /* IS_EXECUTABLE attribute */
1521 int bytesToWrite, bytesWritten;
1524 SET_BUFFER_OBJ(invp->info[i].mask);
1525 if ( aap[i].u.buffer.writable )
1526 SET_WRITE_OBJ(invp->info[i].mask);
1528 /* save original buffer size */
1529 invp->info[i].size = aap[i].u.buffer.size;
1532 * Determine the type of the buffer object.
1533 * Typing based on the object name takes precedence
1534 * over the type "hint".
1536 if ( aap[i].u.buffer.name
1537 || (aap[i].u.buffer.type == NULL) )
1539 tmp = DtDtsBufferToDataType(
1540 aap[i].u.buffer.bp,aap[i].u.buffer.size,
1541 aap[i].u.buffer.name);
1543 * Malloc our own copy of the type string so we won't
1544 * have to worry about when to call DtDtsFreeDataType() later.
1546 invp->info[i].type = XtNewString(tmp);
1547 DtDtsFreeDataType(tmp);
1550 invp->info[i].type = XtNewString(aap[i].u.buffer.type);
1554 * Simply create tmp files for ALL buffers.
1556 * This allows us to work around problems related to client
1557 * programs making subsequent changes to or freeing the memory
1558 * associated with the buffer before we are through with it.
1560 * For actions of type CMD we need to have files anyway.
1562 * Be sure to create tmp files with
1563 * a suffix proper for the buffer type.
1566 /* first determine the permissions for the new tmp file */
1568 DtDtsDataTypeToAttributeValue(invp->info[i].type,
1569 _DtActIS_EXECUTABLE,NULL);
1571 * The tmp file should at LEAST be readable
1573 mode=( S_IRUSR | S_IRGRP | S_IROTH );
1574 if ( aap[i].u.buffer.writable )
1575 mode |= ( S_IWUSR | S_IWGRP | S_IWOTH );
1577 && DtDtsIsTrue(is_executable) )
1578 mode |= ( S_IXUSR | S_IXGRP | S_IXOTH );
1580 DtDtsFreeAttributeValue(is_executable);
1583 if ( aap[i].u.buffer.name )
1586 * Attempt to use the name supplied for the buffer.
1588 invp->info[i].name = _DtActGenerateTmpFile(NULL,
1589 aap[i].u.buffer.name,mode,&fd);
1591 if ( !invp->info[i].name )
1594 * Generate tmp file based on format supplied for the
1597 format = DtDtsDataTypeToAttributeValue(invp->info[i].type,
1598 _DtActNAME_TEMPLATE,NULL);
1600 invp->info[i].name = _DtActGenerateTmpFile(NULL,format,mode,&fd);
1601 DtDtsFreeAttributeValue(format);
1603 if ( !invp->info[i].name )
1606 * Unable to generate usable tmp file name.
1609 * Error message makes assertion message redundant.
1610 * myassert(invp->info[i].name);
1612 TmpFileCreateError(w,actionName,_DtGetDtTmpDir());
1614 RESET_INV_PENDING(invp->state);
1615 SET_INV_ERROR(invp->state);
1616 SET_INV_CANCEL(invp->state);
1623 * Write contents of buffer to temp file
1625 myassert( fd >= 0 );
1626 for ( bytesToWrite = aap[i].u.buffer.size, bytesWritten = 0;
1628 bytesToWrite -= bytesWritten)
1630 bytesWritten = write(fd,aap[i].u.buffer.bp,bytesToWrite);
1631 if ( bytesWritten < 0 )
1633 if (errno == EINTR )
1640 myassert(0 /* Unrecoverable Write Error */);
1641 TmpFileWriteError(w,actionName,invp->info[i].name);
1644 (void) unlink(invp->info[i].name);
1646 RESET_INV_PENDING(invp->state);
1647 SET_INV_ERROR(invp->state);
1648 SET_INV_CANCEL(invp->state);
1658 /* error closing fd */
1659 if ( closeAttempts > _DT_ACTION_MAX_CLOSE_TRIES )
1664 case EBADF: /* invalid fd */
1667 case EINTR: /* interrupted sys call */
1669 continue; /* try again */
1670 case ENOSPC: /* Not enough space on NFS-mounted dev */
1671 TmpFileWriteError(w,actionName,
1672 invp->info[i].name);
1674 unlink(invp->info[i].name);
1675 RESET_INV_PENDING(invp->state);
1676 SET_INV_ERROR(invp->state);
1677 SET_INV_CANCEL(invp->state);
1679 /* try another close */
1682 /* It should have worked this time */
1686 default: /* anything else */
1690 break; /* only try again for conditions with continue */
1695 * Now that we have created a tmp file for this buffer
1696 * object set the FILE_OBJ flag as well as the buffer flag.
1697 * Objects with both the BUFFER and FILE flags set will be
1698 * recognized as buffers which have been written to tmp files.
1701 SET_FILE_OBJ(invp->info[i].mask);
1704 else if ( aap[i].argClass == DtACTION_FILE )
1706 invp->info[i].name = XtNewString(aap[i].u.file.name);
1708 SET_FILE_OBJ(invp->info[i].mask);
1709 SET_WRITE_OBJ(invp->info[i].mask);
1713 myassert( 0 /* unsupported object */ );
1714 UnSupportedObject(w, actionName, aap[i].argClass);
1716 RESET_INV_PENDING(invp->state);
1717 SET_INV_ERROR(invp->state);
1718 SET_INV_CANCEL(invp->state);
1732 ActionRequest * request,
1733 ObjectData * objectData,
1745 char *resolvedPath=NULL;
1747 /********************************************************************
1748 WE NO LONGER ACCEPT host:/path FORMAT
1749 if (host = _DtHostString(filename))
1751 hostId = _DtAddEntry(host, &request->hostNames, &request->numHostNames);
1755 ********************************************************************/
1758 hostId = _DtAddEntry(hostname, &request->hostNames,
1759 &request->numHostNames);
1763 if ( request->cwdHost != NULL )
1765 hostId = _DtAddEntry(request->cwdHost, &request->hostNames,
1766 &request->numHostNames);
1770 /* if all else fails use local host */
1771 host = _DtGetLocalHostName();
1772 hostId = _DtAddEntry(host, &request->hostNames,
1773 &request->numHostNames);
1778 objectData->u.file.origFilename = XtNewString(filename);
1779 objectData->u.file.origHostname = XtNewString(hostname);
1780 objectData->u.file.hostIndex = hostId;
1781 objectData->u.file.baseFilename = _DtBasename(filename);
1782 objectData->type = -1;
1784 /* Hash the directory name */
1785 if ( (dirName = _DtDirname(filename)) == NULL )
1787 if ( request->cwdDir )
1788 dirName = XtNewString(request->cwdDir);
1791 /* Default to current directory */
1792 dirName = _DtFindCwd();
1795 else if ( dirName[0] != '/' )
1798 * We have been provided with a relative path name
1799 * interpret it relative to the context directory.
1803 if ( request->cwdDir )
1804 tmpName=XtNewString(request->cwdDir);
1806 tmpName=_DtFindCwd();
1808 tmpName=XtRealloc(tmpName,strlen(tmpName)+strlen(dirName) +2);
1809 (void)strcat(tmpName,"/");
1810 (void)strcat(tmpName,dirName);
1815 if ( objectData->u.file.baseFilename == NULL || dirName == NULL )
1817 /* Invalidly formed file name */
1818 InvalidFilename(w, request->clonedAction->label, filename);
1819 _DtFreeRequest (request);
1821 XtFree(objectData->u.file.origFilename);
1822 XtFree(objectData->u.file.origHostname);
1823 XtFree(objectData->u.file.baseFilename);
1827 objectData->u.file.dirIndex = _DtAddEntry(dirName, &request->dirNames,
1828 &request->numDirNames);
1829 SET_UNKNOWN_IF_DIR(objectData->mask);
1830 SET_FILE_OBJ(objectData->mask);
1832 * default file objects are treated as writable/returned objects.
1834 SET_WRITE_OBJ(objectData->mask);
1837 * If a type has been provided for this file -- use it.
1838 * otherwise -- look up the type.
1842 if ( filetype && *filetype )
1843 objectData->type = _DtDtsMMStringToBoson(filetype);
1846 TryToTypeFile(objectData,request->hostNames[hostId],
1847 dirName, objectData->u.file.baseFilename,
1853 if ( !stat(resolvedPath,&sbuf) )
1855 /* successful stat of file -- check permissions */
1856 if ( !( sbuf.st_mode&S_IWOTH
1857 || sbuf.st_mode&S_IWGRP
1858 || sbuf.st_mode&S_IWUSR) )
1860 RESET_WRITE_OBJ(objectData->mask);
1867 XtFree(resolvedPath);
1874 * Returns a string representing the current working directory
1875 * for this process. This string must be freed up by the caller.
1876 * NOTE:This function does not replace sym_links with real paths.
1877 * This may be useful on networks where nfs mounts and symbolic
1878 * consistently named symbolic links are used to give the
1879 * impression of a single large network file system.
1887 char buf[MAXPATHLEN + 1];
1889 if ((tmp = getcwd(buf, MAXPATHLEN)) == NULL)
1892 DtProgName, DtError, NULL,
1893 "getcwd(): unable to get current directory", NULL);
1896 return (XtNewString(tmp));
1901 * Generic function which checks to see if the specified string is
1902 * already entered in the passed-in array; if so, then it will return
1903 * the index of the existing entry within the array; if not, then it
1904 * will grow the array, add the string into it, and then return the
1917 /* See if the string is already in the array */
1918 for (i = 0; i < *sizePtr; i++)
1920 if (strcmp(string, (*arrayPtr)[i]) == 0)
1924 /* Add the string */
1927 (*arrayPtr) = (String *)XtRealloc((String)*arrayPtr,
1928 (Cardinal)(sizeof(String) * (*sizePtr)));
1929 (*arrayPtr)[i] = XtNewString(string);
1935 * This function will type the indicated file, only if it is the first
1936 * parameter file; this is to improve performance, since in many cases,
1937 * only the first argument is used to 'type' the action, and the others
1938 * never need to be 'typed'.
1947 char **resolvedPath )
1950 /* Follow the link when typing files */
1951 obj->type = LinkToTypeQuark(host, dir, file, resolvedPath);
1956 * Given a file, follow any links, and base the filetype off of the
1957 * final file, not the link we are passed.
1965 char **resolvedPath )
1969 char link_path[MAXPATHLEN + 1];
1970 char file_name[MAXPATHLEN + 1];
1980 /* Used to check for symbolic link loops */
1983 history = (char **)XtMalloc(sizeof(char *) * history_size);
1985 path = _DtActMapFileName(host, dir, file, NULL);
1991 strcpy(file_name, path);
1994 while ((link_len = readlink(file_name, link_path, MAXPATHLEN)) > 0)
1996 link_path[link_len] = '\0';
1998 /* Force the link to be an absolute path, if necessary */
1999 if (link_path[0] != '/')
2001 /* Relative paths are relative to the current directory */
2002 end = DtStrrchr(file_name, '/') + 1;
2004 strcat(file_name, link_path);
2007 strcpy(file_name, link_path);
2009 /* Check for a recursive loop; abort if found */
2010 for (i = 0; i < history_count; i++)
2012 if (strcmp(file_name, history[i]) == 0)
2014 /* Drop back to last non-recursive portion of the path */
2015 strcpy(file_name, history[history_count-1]);
2016 for (i = 0; i < history_count; i++)
2018 XtFree((char *)history);
2019 dtype = DtDtsFileToDataType(file_name);
2020 dquark = _DtDtsMMStringToBoson(dtype);
2021 DtDtsFreeDataType(dtype);
2022 *resolvedPath = XtNewString(file_name);
2027 /* Add to the history list */
2028 if (history_count >= history_size)
2030 history_size += 100;
2031 history = (char **)XtRealloc((char *)history,
2032 sizeof(char *) * history_size);
2034 history[history_count++] = XtNewString(file_name);
2037 /* Free up the history list */
2038 for (i = 0; i < history_count; i++)
2040 XtFree((char *)history);
2042 dtype = DtDtsFileToDataType(file_name);
2043 dquark = _DtDtsMMStringToBoson(dtype);
2044 DtDtsFreeDataType(dtype);
2045 *resolvedPath = XtNewString(file_name);
2051 * Given a request, find the action to which it maps, and see if enough
2052 * parameters were supplied to allow the action to be started. It's
2053 * possible we may need to bring up a dialog to collect more data, or
2054 * we may need to invoke multiple actions.
2056 * The first time an action request is processed, we will check the
2057 * parameter situation, and will prompt the user, if necessary. The
2058 * second time the action request is processed (typically when the
2059 * user closes the parameter collecting dialog), we will simply invoke
2060 * the action with whatever we have; the user will not be prompted a
2061 * second time for any missing parameters.
2063 * If the request is processed (True is returned), then it is up to the
2064 * caller to free up the request structure.
2070 ActionRequest *request )
2076 PromptEntry * prompts;
2077 DtShmBoson actionQuark;
2078 Tt_status status = TT_OK;
2080 /* See if this is the first pass for the request */
2081 if (request->clonedAction == NULL)
2084 /* Always start with the first host, when processing a request */
2085 request->hostIndex = 0;
2087 /* Find the action DB entry which we map to */
2088 actionQuark = _DtDtsMMStringToBoson(request->actionName);
2089 RESET_TOO_MANY_MAPS(request->mask);
2091 if (actionQuark == -1 || (action = _DtActionFindDBEntry(request, actionQuark)) == NULL)
2094 * No action label is available here for error dialogs
2096 if (IS_TOO_MANY_MAPS(request->mask))
2098 MapError (w, request->actionName);
2101 else if (request->numObjects > 0)
2103 if (IS_FILE_OBJ(request->objects[0].mask))
2105 NoActionError(w, actionQuark,
2106 request->actionName,
2107 (char *)_DtDtsMMBosonToString(request->objects[0].type),
2108 request->hostNames[request->objects[0].u.file.hostIndex],
2109 request->dirNames[request->objects[0].u.file.dirIndex],
2110 request->objects[0].u.file.baseFilename);
2112 else if ( IS_BUFFER_OBJ(request->objects[0].mask) )
2115 * RWV -- may have to modify this call to generate a
2116 * message more suitable for buffer objects.
2118 NoActionError(w, actionQuark,
2119 request->actionName,
2120 (char *)_DtDtsMMBosonToString(request->objects[0].type),
2123 "Memory Object" /* filename */);
2125 myassert(0 /* should never get here */ );
2127 /* fdt: add code for strings
2128 * else if (IS_STRING_OBJ(request->objects[0].mask))
2132 NoActionError(w, actionQuark, request->actionName,
2133 NULL, NULL, NULL, NULL);
2136 * If we are in the middle of reprocessing a single argument
2137 * action, then continue with the next parameter. Otherwise,
2138 * this error terminates the request, so return.
2140 if (IS_REPROCESSING(request->mask) && MoreArgumentsToProcess(request))
2141 return(ProcessRequest(w, request));
2144 * We were never able to start this action.
2147 _DtActInvRecT *invRecP = _DtActFindInvRec(request->invocId);
2148 if (invRecP) SET_INV_ERROR(invRecP->state);
2153 request->clonedAction = action;
2156 * If this is a ToolTalk message, then before proceeding any further,
2157 * make sure we can get connected to a ToolTalk session. If we can't,
2158 * then we need to bail out.
2160 if (IS_TT_MSG(action->mask) &&
2161 (status = _DtInitializeToolTalk(NULL)) != TT_OK)
2163 NoToolTalkConnectionError(w, request->clonedAction->label, status);
2165 _DtActInvRecT *invRecP = _DtActFindInvRec(request->invocId);
2166 if (invRecP) SET_INV_ERROR(invRecP->state);
2171 /* Determine how we are sitting with parameters */
2172 unused = MatchParamsToAction(request, &numPrompts, &prompts);
2173 request->objsUsed = request->numObjects - unused;
2174 myassert(request->objsUsed >= 0);
2177 * Do we need to create a prompt dialog?
2178 * NOTE: if the action requires the user to be prompted, but the
2179 * user has supplied extra parameters, so he will be asked
2180 * to abort or continue, do the abort/continue dialog BEFORE
2181 * the prompt dialog; there's little sense in collecting
2182 * additional input if the user is going to abort the action!
2184 if ((prompts != NULL) &&
2185 ((unused == 0) || IS_ARG_SINGLE_ARG(action->mask) ||
2186 IS_ARG_NONE_FOUND(action->mask)))
2188 CreatePromptDialog(w, request, numPrompts, prompts);
2189 XtFree((char *)prompts);
2193 /* Were too many parameters supplied? */
2194 else if (unused > 0)
2197 * If the action only needs a single parameter, then we need
2198 * to fire off multiple instances of the action; otherwise,
2199 * prompt the user to continue or abort. An action requiring
2200 * no parameters is also treated like a single parameter action.
2202 if (IS_ARG_SINGLE_ARG(action->mask) || IS_ARG_NONE_FOUND(action->mask))
2205 PrepareAndExecuteAction(w, request);
2207 /* See if there are still more parameters to be processed */
2208 if (MoreArgumentsToProcess(request))
2209 return(ProcessRequest(w, request));
2214 * Postpone any further processing until the user either
2215 * tells us to continue, or abort.
2217 CreateContinueDialog(w, request, numPrompts, prompts);
2218 XtFree((char *)prompts);
2224 PrepareAndExecuteAction(w, request);
2230 PrepareAndExecuteAction(w, request);
2231 action = request->clonedAction;
2234 * If this is a single argument action, and we have more parameters
2235 * waiting to be processed, then continue processing them.
2237 if ((IS_ARG_SINGLE_ARG(action->mask) || IS_ARG_NONE_FOUND(action->mask))
2238 && (MoreArgumentsToProcess(request)))
2240 return(ProcessRequest(w, request));
2249 * This function is called at the point where we have collected all of the
2250 * information needed to actually initiate the action. We will use the
2251 * set of arguments passed into _DtActionInvoke(), along with any values
2252 * supplied through the prompt dialog. It is also at this point that
2253 * the thread of control will split, dependent upon the type of action
2254 * being executed (Command Invoker, Tooltalk).
2257 PrepareAndExecuteAction(
2259 ActionRequest *request )
2265 ActionPtr action = request->clonedAction;
2267 _DtActInvRecT *invp; /* pointer to invocation record */
2268 _DtActChildRecT *childrecp; /* pointer to child record */
2271 * We have gathered all the information necessary to invoke
2272 * this action all dialogs have been posted and processed.
2273 * Now create the action invocation record -- unless we are
2274 * in the midst of reprocessing an already invoked action.
2276 invp = _DtActFindInvRec(request->invocId);
2278 SET_INV_WORKING(invp->state);
2281 * Allocate a child rec -- fill it in
2283 if ( (childrecp = _DtActAllocChildRec(invp)) != NULL )
2285 request->childId = childrecp->childId;
2287 childrecp->childState = _DtActCHILD_PENDING_START;
2288 childrecp->mask = action->mask;
2290 childrecp->numObjects = request->objsUsed;
2293 myassert( 0 /* Unable to allocate childRec */ );
2297 * Before proceeding, we need to determine what host and directory
2298 * will be used when resolving relative pathnames.
2300 __ExtractCWD(request, &relPathHost, &relPathDir, False);
2301 if (IS_CMD(action->mask))
2304 * All buffer objects must be placed into temporary files for
2305 * command actions. This has already been done when the
2306 * request structure was created.
2308 if (childrecp && childrecp->numObjects > 0)
2311 (_DtActArgMap *)XtCalloc(childrecp->numObjects,
2312 sizeof(_DtActArgMap));
2314 for ( i = 0; i < childrecp->numObjects && i < invp->ac; i++ )
2316 childrecp->argMap[i].argN = i+1; /* ignored for CMD actions */
2317 childrecp->argMap[i].argIdx =
2318 i + request->objOffset; /* idx into invp->info[] */
2321 ProcessCommandInvokerRequest(w, request, relPathHost, relPathDir);
2323 else if (IS_TT_MSG(action->mask))
2328 * create argmap for returnable arguments --
2329 * i.e. those appearing in TT_ARGn_VALUE fields.
2331 * The requirement is that one and only one action argument may
2332 * appear in a TT_ARGn_VALUE field.
2334 * argMap is a sparse array which maps TT_ARGn_VALUEs to input
2335 * parameters. If a TT_ARGn_VALUE does not have an input parameter
2336 * as a value then the sentinel value "-1" is provided as the index.
2337 * Allocate enough space for all the TT_ARGn_VALUEs plus one for
2340 * The elements of the argMap array then represent:
2341 * argMap[ TT_ARG0, TT_ARG1, ...,TT_ARGN, TT_FILE]
2345 (_DtActArgMap *)XtCalloc( action->u.tt_msg.value_count + 1,
2346 sizeof(_DtActArgMap));
2348 for ( i = 0; i < action->u.tt_msg.value_count; i++)
2351 * Set index value to "-1". This value will indicate
2352 * TT_ARGn_VALUES which are NOT associated with input
2353 * parameters (action arguments). If there is an action
2354 * argument associated with this TT_ARGn_VALUE we will set
2357 childrecp->argMap[i].argIdx = -1;
2358 childrecp->argMap[i].argN = i;
2360 /* null argn value is valid -- so check MsgParts*/
2361 if (!action->u.tt_msg.tt_argn_value[i].numMsgParts)
2365 u.tt_msg.tt_argn_value[i].parsedMessage[0].keyword
2370 * TT_ARGn_VALUE fields should have only one arg keyword.
2372 myassert(action->u.tt_msg.tt_argn_value[i].numMsgParts == 1);
2374 u.tt_msg.tt_argn_value[i].parsedMessage[0].argNum;
2376 if ( ( argNum > 0 ) && ( argNum <= invp->ac ) )
2378 /* The ith message part must be returned */
2379 childrecp->argMap[i].argIdx =
2380 argNum + request->objOffset - 1;
2381 myassert( childrecp->argMap[i].argIdx >= 0 );
2385 * Add an argMap entry for the value of the TT_FILE field.
2386 * Tooltalk (e.g. media messages) sometimes uses this field
2387 * to pass values such as file names to the message receipient.
2388 * If the TT_FILE field has a single ARG keyword
2389 * then record that parameter number otherwise record "-1" as
2390 * was done for the value arguments above.
2392 childrecp->argMap[i].argIdx = -1;
2393 childrecp->argMap[i].argN = -1; /* Use "-1" as TT_FILE entry idx */
2394 if (action->u.tt_msg.tt_file.numMsgParts
2395 && action->u.tt_msg.tt_file.parsedMessage[0].keyword == ARG )
2398 u.tt_msg.tt_file.parsedMessage[0].argNum;
2399 if ( ( argNum > 0 ) && ( argNum <= invp->ac ) )
2401 /* The ith message part should be the last argMap entry */
2402 childrecp->argMap[i].argIdx =
2403 argNum + request->objOffset - 1;
2404 myassert( childrecp->argMap[i].argIdx >= 0 );
2409 _DtProcessTtRequest(w, request, relPathHost, relPathDir);
2413 * For now we are through invoking this child.
2414 * There may still be more children to invoke or we may have to
2415 * re-invoke this child (e.g. multi-host processing for commands).
2417 SET_INV_DONE(invp->state);
2419 /* Free up the path information */
2420 XtFree(relPathHost);
2426 * Determine the CWD to use; this information can be used to both
2427 * resolve relative filepaths, and to set the CWD used when executing
2428 * a command invoker request. When resolving relative paths, the
2429 * information specified for the first argument is not used (see case 2
2430 * below). It is determined using the following algorithm:
2432 * 1) Use the CWD specified in the action (if a cmd invoker action).
2433 * 2) If told to use the objects, then use the directory where the
2434 * object lives (if a regular file), or the object itself (if
2436 * 3) Use the CWD passed into _) by the application.
2437 * 4) Use the physical CWD of the application.
2439 * Both the host and directory paths must by freed by the caller.
2443 ActionRequest *request,
2446 Boolean useObjectInfo )
2452 ActionPtr action = request->clonedAction;
2454 /* Only dropped objects will have been 'typed' at this point */
2455 if (useObjectInfo && (IS_CMD(action->mask)) && (request->numObjects > 0) &&
2456 (request->objects[0].type >= 0) &&
2457 IS_FILE_OBJ(request->objects[0].mask))
2459 if (action->u.cmd.contextHost != NULL)
2460 *hostPtr = XtNewString(action->u.cmd.contextHost);
2463 *hostPtr = XtNewString(
2464 request->hostNames[request->objects[0].u.file.hostIndex]);
2467 if (IS_UNKNOWN_IF_DIR(request->objects[0].mask))
2470 char *theHost, *theDir;
2471 struct stat statInfo;
2473 RESET_UNKNOWN_IF_DIR(request->objects[0].mask);
2476 * The file may not have been checked yet, if it was never
2477 * referenced in the execution string; so .. we'll check
2480 theHost = request->hostNames[request->objects[0].u.file.hostIndex];
2481 theDir = request->dirNames[request->objects[0].u.file.dirIndex];
2482 nfsPath = _DtActMapFileName(theHost, theDir,
2483 request->objects[0].u.file.baseFilename, NULL);
2485 if (nfsPath && (stat(nfsPath, &statInfo) == 0) &&
2486 ((statInfo.st_mode & S_IFMT) == S_IFDIR))
2488 SET_DIR_OBJ(request->objects[0].mask);
2493 if (IS_DIR_OBJ(request->objects[0].mask))
2495 if (action->u.cmd.contextDir != NULL)
2496 *dirPtr = XtNewString(action->u.cmd.contextDir);
2499 *dirPtr = XtMalloc((Cardinal)
2500 (strlen(request->dirNames[request->objects[0].u.file.dirIndex]) +
2501 strlen(request->objects[0].u.file.baseFilename) + 2));
2503 request->dirNames[request->objects[0].u.file.dirIndex]);
2505 DtLastChar(*dirPtr, &lastCh, &lastChLen);
2506 if ((lastChLen != 1) || (*lastCh != '/'))
2507 (void)strcat(*dirPtr, "/");
2509 (void)strcat(*dirPtr, request->objects[0].u.file.baseFilename);
2514 if (action->u.cmd.contextDir != NULL)
2515 *dirPtr = XtNewString(action->u.cmd.contextDir);
2518 *dirPtr = XtNewString(
2519 request->dirNames[request->objects[0].u.file.dirIndex]);
2525 /* Use specified context, or get process context, if necessary */
2526 if (IS_CMD(action->mask) && (action->u.cmd.contextHost != NULL))
2527 *hostPtr = XtNewString(action->u.cmd.contextHost);
2528 else if (request->cwdHost)
2529 *hostPtr = XtNewString(request->cwdHost);
2532 *hostPtr = _DtGetLocalHostName();
2535 if (IS_CMD(action->mask) && (action->u.cmd.contextDir != NULL))
2536 *dirPtr = XtNewString(action->u.cmd.contextDir);
2537 else if (request->cwdDir)
2538 *dirPtr = XtNewString(request->cwdDir);
2540 *dirPtr = _DtFindCwd();
2546 * This function is used to prepare for the continued processing of
2547 * the parameters, when the action is a single argument action. It
2548 * free up any data which was associated with the previous parameter,
2549 * and cascades up any remaining parameters in the object array.
2553 MoreArgumentsToProcess(
2554 ActionRequest *request )
2561 if (request->numObjects <= 1)
2567 /* Repeat processing for the next argument */
2569 /* Cascade up the remaining, unprocess parameters */
2570 if (IS_FILE_OBJ(request->objects[0].mask))
2572 XtFree(request->objects[0].u.file.origFilename);
2573 XtFree(request->objects[0].u.file.origHostname);
2574 XtFree(request->objects[0].u.file.baseFilename);
2578 * Since we use tmp files for buffers and do not support
2579 * strings; we need not add special code for buffer and
2582 /* fdt: Add support for strings and buffers here
2583 * else if (IS_BUFFER_OBJ(request->objects[0].mask))
2584 * XtFree(request->objects[0].u.buffer.buffer);
2585 * else if (IS_STRING_OBJ(request->objects[0].mask))
2586 * XtFree(request->objects[0].u.string.string);
2588 for (i = 0; i < (request->numObjects - 1); i++)
2590 request->objects[i] = request->objects[i+1];
2592 request->numObjects--;
2593 request->objOffset++;
2594 request->objsUsed = 0;
2595 request->childId = 0;
2597 /* Free up our previously cloned action */
2598 _DtFreeActionStruct(request->clonedAction);
2599 request->clonedAction = NULL;
2601 /* Free up any leftover prompt strings */
2602 for (i = 0; i < request->numPromptInputs; i++)
2603 XtFree(request->promptInputs[i]);
2604 XtFree((char *)request->promptInputs);
2605 request->promptInputs = NULL;
2606 request->numPromptInputs = 0;
2607 SET_REPROCESSING(request->mask);
2608 XtFree(request->badHostList);
2609 request->badHostList = NULL;
2610 XtFree(request->currentHost);
2611 request->currentHost = NULL;
2612 request->hostIndex = 0;
2614 /* Type the object, if possible */
2615 if (IS_FILE_OBJ(request->objects[0].mask))
2617 if ((request->objects[0].u.file.hostIndex >= 0) &&
2618 (request->objects[0].u.file.dirIndex >= 0))
2620 path = _DtActMapFileName(
2621 request->hostNames[request->objects[0].u.file.hostIndex],
2622 request->dirNames[request->objects[0].u.file.dirIndex],
2623 request->objects[0].u.file.baseFilename, NULL);
2624 dtype = DtDtsFileToDataType(path);
2625 request->objects[0].type = _DtDtsMMStringToBoson(dtype);
2626 DtDtsFreeDataType(dtype);
2632 * Since we use tmp files for buffers and do not support
2633 * strings; we need not add special code for buffer and
2636 /* fdt: add support for buffers and strings here
2637 * else if (IS_BUFFER_OBJ(request->objects[0].mask))
2640 * else if (IS_STRING_OBJ(request->objects[0].mask))
2650 /***************************************************************************/
2651 /***************************************************************************/
2652 /* Functions For Cloning And Free Structures */
2653 /***************************************************************************/
2654 /***************************************************************************/
2658 * At the point that a request is sent, we need to save a copy of the
2659 * request, for future reference. We need to clone the request, since
2660 * the original request structure may get modified between the time we
2661 * send the message, and the time we need to reference it in the future.
2666 ActionRequest * request)
2669 ActionRequest * newRequest;
2672 newRequest = (ActionRequest *)XtMalloc((Cardinal)sizeof(ActionRequest));
2675 * Structure assignment to clone all scalar values
2676 * If a value is not explicitly set then it defaults to the same value
2677 * as in the original request. Pointers to malloc-ed memory should all
2678 * be replaced with pointers to a copy of the region.
2680 (*newRequest) = (*request);
2682 newRequest->actionName = XtNewString(request->actionName);
2684 if (request->numObjects > 0) {
2685 newRequest->objects = (ObjectData *)XtMalloc(sizeof(ObjectData) *
2686 request->numObjects);
2688 for (i = 0; i < request->numObjects; i++)
2690 newRequest->objects[i] = request->objects[i];
2691 if (IS_FILE_OBJ(request->objects[i].mask))
2693 newRequest->objects[i].u.file.origFilename =
2694 XtNewString(request->objects[i].u.file.origFilename);
2695 newRequest->objects[i].u.file.origHostname =
2696 XtNewString(request->objects[i].u.file.origHostname);
2697 newRequest->objects[i].u.file.baseFilename =
2698 XtNewString(request->objects[i].u.file.baseFilename);
2700 else if ( IS_BUFFER_OBJ(request->objects[i].mask) )
2704 * Since we are creating tmp files for all buffers
2705 * we should never have to copy a buffer's contents.
2707 * We should never reach this code because the FILE_OBJ
2708 * bit is set when we create tmp files for buffers.
2713 * Can we get by without copying buffer object contents?
2714 * if so -- how do we avoid freeing it twice OR
2715 * not freeing it at all?
2717 /* make a copy of the buffer */
2718 if ( request->objects[i].u.buffer.bp )
2720 myassert(newRequest->objects[i].u.buffer.size == request->objects[i].u.buffer.size);
2721 newRequest->objects[i].u.buffer.bp =
2722 XtMalloc( request->objects[i].u.buffer.size );
2723 memcpy(newRequest->objects[i].u.buffer.bp,
2724 request->objects[i].u.buffer.bp,
2725 newRequest->objects[i].u.buffer.size);
2729 myassert(0 /* no other object types supported */ );
2733 newRequest->numPromptInputs = request->numPromptInputs;
2734 if (request->numPromptInputs > 0) {
2735 newRequest->promptInputs = (char **)XtMalloc(sizeof(char *) *
2736 request->numPromptInputs);
2737 for (i = 0; i < request->numPromptInputs; i++)
2738 newRequest->promptInputs[i] = XtNewString(request->promptInputs[i]);
2741 newRequest->numHostNames = request->numHostNames;
2742 if (request->numHostNames > 0) {
2743 newRequest->hostNames = (char **)XtMalloc(sizeof(char *) *
2744 request->numHostNames);
2745 for (i = 0; i < request->numHostNames; i++)
2746 newRequest->hostNames[i] = XtNewString(request->hostNames[i]);
2749 newRequest->numDirNames = request->numDirNames;
2750 if (request->numDirNames > 0) {
2751 newRequest->dirNames = (char **)XtMalloc(sizeof(char *) *
2752 request->numDirNames);
2753 for (i = 0; i < request->numDirNames; i++)
2754 newRequest->dirNames[i] = XtNewString(request->dirNames[i]);
2757 newRequest->termOpts = XtNewString(request->termOpts);
2758 newRequest->cwdHost = XtNewString(request->cwdHost);
2759 newRequest->cwdDir = XtNewString(request->cwdDir);
2761 if (request->clonedAction)
2762 newRequest->clonedAction = CloneActionDBEntry(request->clonedAction);
2764 newRequest->clonedAction = NULL;
2766 newRequest->badHostList = XtNewString(request->badHostList);
2767 newRequest->currentHost = XtNewString(request->currentHost);
2768 newRequest->execHost = XtNewString(request->execHost);
2776 * Free up the contents of a request structure
2781 ActionRequest *request )
2786 XtFree(request->actionName);
2788 for (i = 0; i < request->numObjects; i++)
2790 if (IS_FILE_OBJ(request->objects[i].mask))
2792 XtFree(request->objects[i].u.file.origFilename);
2793 XtFree(request->objects[i].u.file.origHostname);
2794 XtFree(request->objects[i].u.file.baseFilename);
2798 * Since we use tmp files for buffers and do not support
2799 * strings; we need not add special code for buffer and
2802 /* fdt: Add support for buffers and strings here
2803 * else if (IS_BUFFER_OBJ(request->objects[i].mask)
2804 * XtFree(request->objects[i].u.buffer.buffer);
2805 * else if (IS_STRING_OBJ(request->objects[i].mask)
2806 * XtFree(request->objects[i].u.string.string);
2811 * Since the objectDataArray was malloced at once
2812 * we can free it at once.
2814 if (request->objects) XtFree((char *)request->objects);
2816 for (i = 0; i < request->numPromptInputs; i++)
2817 XtFree(request->promptInputs[i]);
2818 if (request->promptInputs) XtFree((char *)request->promptInputs);
2820 for (i = 0; i < request->numHostNames; i++)
2821 XtFree(request->hostNames[i]);
2822 if (request->hostNames) XtFree((char *)request->hostNames);
2824 for (i = 0; i < request->numDirNames; i++)
2825 XtFree(request->dirNames[i]);
2826 if (request->dirNames) XtFree((char *)request->dirNames);
2828 XtFree(request->termOpts);
2829 XtFree(request->cwdHost);
2830 XtFree(request->cwdDir);
2831 _DtFreeActionStruct(request->clonedAction);
2832 XtFree(request->badHostList);
2833 XtFree(request->currentHost);
2834 XtFree(request->execHost);
2836 XtFree ((char *)request);
2841 * Create a clone of an action DB entry
2849 ActionPtr newAction = (ActionPtr)XtMalloc((Cardinal)sizeof(Action));
2852 /* Clone each field */
2853 newAction->action = action->action;
2854 newAction->file_name_id = action->file_name_id;
2855 newAction->label = XtNewString(action->label);
2856 newAction->description = XtNewString(action->description);
2858 newAction->type_count = action->type_count;
2859 if (action->type_count > 0) {
2860 newAction->arg_types = (DtShmBoson *)XtMalloc(sizeof(DtShmBoson) *
2861 newAction->type_count);
2862 for (i = 0; i < newAction->type_count; i++)
2863 newAction->arg_types[i] = action->arg_types[i];
2866 newAction->arg_types = NULL;
2869 newAction->arg_count = action->arg_count;
2870 newAction->mask = action->mask;
2872 if (IS_CMD(action->mask))
2874 cmdAttr * newCmd = &(newAction->u.cmd);
2875 cmdAttr * oldCmd = &(action->u.cmd);
2877 CloneParsedMessage(&(oldCmd->execString), &(newCmd->execString));
2878 CloneParsedMessage(&(oldCmd->termOpts), &(newCmd->termOpts));
2879 newCmd->contextDir = XtNewString(oldCmd->contextDir);
2880 newCmd->contextHost = XtNewString(oldCmd->contextHost);
2881 CloneParsedMessage(&(oldCmd->execHosts), &(newCmd->execHosts));
2882 newCmd->execHostCount = oldCmd->execHostCount;
2883 if (oldCmd->execHostCount > 0) {
2884 newCmd->execHostArray = (char **)XtMalloc(sizeof(char *) *
2885 newCmd->execHostCount);
2886 for (i = 0; i < newCmd->execHostCount; i++)
2887 newCmd->execHostArray[i] = XtNewString(oldCmd->execHostArray[i]);
2890 newCmd->execHostArray = NULL;
2893 else if (IS_MAP(action->mask))
2895 newAction->u.map.map_action = action->u.map.map_action;
2897 else if (IS_TT_MSG(action->mask))
2899 tt_msgAttr * newMsg = &(newAction->u.tt_msg);
2900 tt_msgAttr * oldMsg = &(action->u.tt_msg);
2902 newMsg->tt_class = oldMsg->tt_class;
2903 newMsg->tt_scope = oldMsg->tt_scope;
2904 CloneParsedMessage(&(oldMsg->tt_op), &(newMsg->tt_op));
2905 CloneParsedMessage(&(oldMsg->tt_file), &(newMsg->tt_file));
2907 newMsg->mode_count = oldMsg->mode_count;
2908 if (oldMsg->mode_count > 0) {
2909 newMsg->tt_argn_mode =
2910 (int *)XtMalloc(sizeof(int) * newMsg->mode_count);
2911 for (i = 0; i < newMsg->mode_count; i++)
2912 newMsg->tt_argn_mode[i] = oldMsg->tt_argn_mode[i];
2915 newMsg->tt_argn_mode = NULL;
2918 newMsg->vtype_count = oldMsg->vtype_count;
2919 newMsg->tt_argn_vtype = CloneParsedMessageArray(oldMsg->tt_argn_vtype,
2920 oldMsg->vtype_count);
2922 newMsg->value_count = oldMsg->value_count;
2923 newMsg->tt_argn_value = CloneParsedMessageArray(oldMsg->tt_argn_value,
2924 oldMsg->value_count);
2926 newMsg->rep_type_count = oldMsg->rep_type_count;
2927 if (oldMsg->rep_type_count > 0) {
2928 newMsg->tt_argn_rep_type = (int *)XtMalloc(sizeof(int) *
2929 newMsg->rep_type_count);
2930 for (i = 0; i < newMsg->rep_type_count; i++)
2931 newMsg->tt_argn_rep_type[i] = oldMsg->tt_argn_rep_type[i];
2934 newMsg->tt_argn_rep_type = NULL;
2943 * Free up the contents of a request structure
2947 _DtFreeActionStruct(
2956 XtFree(action->label);
2957 XtFree(action->description);
2958 if (action->arg_types) XtFree((char *)action->arg_types);
2960 if (IS_CMD(action->mask))
2962 FreeParsedMessage(&(action->u.cmd.execString));
2963 FreeParsedMessage(&(action->u.cmd.termOpts));
2964 XtFree(action->u.cmd.contextDir);
2965 XtFree(action->u.cmd.contextHost);
2966 FreeParsedMessage(&(action->u.cmd.execHosts));
2967 for (i = 0; i < action->u.cmd.execHostCount; i++)
2968 XtFree(action->u.cmd.execHostArray[i]);
2969 if (action->u.cmd.execHostArray) {
2970 XtFree((char *)action->u.cmd.execHostArray);
2973 else if (IS_TT_MSG(action->mask))
2975 FreeParsedMessage(&(action->u.tt_msg.tt_op));
2976 FreeParsedMessage(&(action->u.tt_msg.tt_file));
2977 if (action->u.tt_msg.tt_argn_mode) {
2978 XtFree((char *)action->u.tt_msg.tt_argn_mode);
2980 FreeParsedMessageArray(action->u.tt_msg.tt_argn_vtype,
2981 action->u.tt_msg.vtype_count);
2982 FreeParsedMessageArray(action->u.tt_msg.tt_argn_value,
2983 action->u.tt_msg.value_count);
2984 if (action->u.tt_msg.tt_argn_rep_type) {
2985 XtFree((char *)action->u.tt_msg.tt_argn_rep_type);
2989 XtFree((char *)action);
2995 parsedMsg * old_pmsg,
2996 parsedMsg * new_pmsg )
3000 MsgComponent * piece;
3001 MsgComponent * newPiece;
3003 new_pmsg->numMsgParts = old_pmsg->numMsgParts;
3004 if (old_pmsg->compiledMessage)
3007 * Some day these may not always be null-terminated strings
3009 new_pmsg->compiledMessage = (char *)XtMalloc(old_pmsg->msgLen);
3010 memcpy(new_pmsg->compiledMessage,
3011 old_pmsg->compiledMessage,
3013 new_pmsg->msgLen = old_pmsg->msgLen;
3017 new_pmsg->compiledMessage = NULL;
3018 new_pmsg->msgLen = 0;
3022 /* Clone the message components */
3023 if (old_pmsg->numMsgParts > 0)
3025 new_pmsg->parsedMessage = (MsgComponent *)
3026 XtMalloc((Cardinal)(sizeof(MsgComponent) * old_pmsg->numMsgParts));
3028 for (i = 0; i < old_pmsg->numMsgParts; i++)
3030 piece = &(old_pmsg->parsedMessage[i]);
3031 newPiece = &(new_pmsg->parsedMessage[i]);
3033 /* Clone each subcomponent of this message */
3034 if (piece->precedingText)
3035 newPiece->precedingText = XtNewString(piece->precedingText);
3037 newPiece->precedingText = NULL;
3040 newPiece->prompt = XtNewString(piece->prompt);
3042 newPiece->prompt = NULL;
3044 newPiece->keyword = piece->keyword;
3045 newPiece->argNum = piece->argNum;
3046 newPiece->mask = piece->mask;
3050 new_pmsg->parsedMessage = NULL;
3055 * Free up the contents of a parsedMsg structure, but not the structure
3056 * itself (since many of our structures contain in-line instances of
3057 * the parsedMsg structure).
3061 parsedMsg * parsedMessage )
3066 /* Free up the message components */
3067 if (parsedMessage->numMsgParts > 0)
3069 for (i = 0; i < parsedMessage->numMsgParts; i++)
3071 XtFree(parsedMessage->parsedMessage[i].precedingText);
3072 XtFree(parsedMessage->parsedMessage[i].prompt);
3074 XtFree((char *)parsedMessage->parsedMessage);
3077 XtFree(parsedMessage->compiledMessage);
3082 * Allocate an array to hold a copy of all of the parsedMsg structures.
3083 * This array must be freed eventually by the caller.
3086 CloneParsedMessageArray(
3087 parsedMsg * pmsgArray,
3091 parsedMsg * newArray;
3097 newArray = (parsedMsg *)XtMalloc(sizeof(parsedMsg) * count);
3099 for (i = 0; i < count; i++)
3100 CloneParsedMessage(pmsgArray + i, newArray + i);
3107 * Free up the counted array of parsedMsg structures.
3108 * The array pointing to them also needs to be freed.
3111 FreeParsedMessageArray(
3112 parsedMsg * parsedMessageArray,
3118 for (i = 0; i < count; i++)
3119 FreeParsedMessage(parsedMessageArray + i);
3121 XtFree((char *)parsedMessageArray);
3125 /***************************************************************************/
3126 /***************************************************************************/
3127 /* Functions For Placing Arguments Into A Message String */
3128 /***************************************************************************/
3129 /***************************************************************************/
3133 * This function takes a 'parsedMsg' structure, and compiles all of its
3134 * pieces into a single string, replacing keywords as they are encountered.
3135 * Since a given action request can be made up of multiple pieces, this
3136 * function uses some static variables to maintain state information between
3137 * calls for the same action request; passing in 'True' for the 'initialize'
3138 * parameter for the first call for a given action request will clear out
3139 * any old static values.
3143 _DtCompileMessagePiece(
3145 ActionRequest *request,
3150 unsigned long processingMask,
3151 Boolean ** paramUsed,
3152 int * promptDataIndex )
3156 Boolean firstParmUsed;
3157 MsgComponent * segment;
3158 char * compiledMsg = NULL;
3159 int compiledMsgSize = 0;
3160 ObjectData tmpObjData;
3161 static char *sessionHostName= NULL;
3162 static char *displayHostName = NULL;
3163 static char *localHostName = NULL;
3165 XtFree(piece->compiledMessage);
3166 piece->compiledMessage = NULL;
3172 * Keep track of which parameters have been used, so that when
3173 * a %Args% keyword is encountered, we know which parameters
3174 * should be substituted.
3176 *promptDataIndex = 0;
3178 if (request->numObjects > 0) {
3179 *paramUsed = (Boolean *)XtMalloc((Cardinal)(sizeof(Boolean) *
3180 request->numObjects));
3181 for (i = 0; i < request->numObjects; i++)
3182 (*paramUsed)[i] = False;
3186 _DtSvcProcessLock();
3187 /* We need to query our hostname the first time only */
3188 if ( ! localHostName )
3189 localHostName = _DtGetLocalHostName();
3192 * Determine the display host name -- default to localHostName for
3193 * degenerate display names (i.e. :0, unix:0, local:0, ...)
3195 if ( ! displayHostName )
3196 displayHostName = _DtGetDisplayHostName(XtDisplay(w));
3198 if ( ! sessionHostName )
3199 sessionHostName = _DtGetSessionHostName();
3200 _DtSvcProcessUnlock();
3203 * The message is constructed by taking each of the
3204 * action segments, replacing any keywords, and then adding the
3205 * information to the end of the buffer.
3207 for (i = 0; i < piece->numMsgParts; i++)
3209 segment = piece->parsedMessage + i;
3211 /* Add any text preceding the keyword */
3212 if (segment->precedingText)
3214 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3215 (int)strlen(segment->precedingText));
3216 (void)strcat(compiledMsg, segment->precedingText);
3219 /* Process the keyword */
3220 switch (segment->keyword)
3224 /* Add in the local host name */
3225 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3226 (int)strlen(localHostName));
3227 (void)strcat(compiledMsg, localHostName);
3234 * Add in the host associated with the DB file from which this
3235 * action was loaded.
3240 fullPath = _DtDbPathIdToString(request->clonedAction->file_name_id);
3241 host = _DtHostString(fullPath);
3244 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3245 host ? (int)strlen(host) : 0);
3246 (void)strcat(compiledMsg, host);
3256 * Use the displayHostName determined the first time thru
3258 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3259 (int)strlen(displayHostName));
3260 (void)strcat(compiledMsg, displayHostName);
3267 * Add in the session server host where providing the
3268 * display management. (i.e. the host where the login client
3271 compiledMsg = GrowMsgBuffer(compiledMsg, &compiledMsgSize,
3272 (int)strlen(sessionHostName));
3273 (void)strcat(compiledMsg, sessionHostName);
3281 * If this is an entry which simply collected some user input,
3282 * then add the user's input to the message buffer.
3283 * This corresponds to the keywords:
3286 * %(String)"prompt"%
3288 if (segment->prompt)
3290 /* Create dummy object; makes processing easier */
3291 if (ParseFileArgument(w, request, &tmpObjData,
3292 NULL, request->promptInputs[*promptDataIndex],
3295 XtFree(compiledMsg);
3299 if (!InsertArgumentString(w, &compiledMsg, &compiledMsgSize,
3300 request, &tmpObjData, segment->mask, relPathHost,
3301 relPathDir, False, 0))
3303 XtFree(compiledMsg);
3307 /* Signal that this prompt has been used */
3308 (*promptDataIndex)++;
3315 if (segment->argNum == ALL_ARGS)
3317 /* Insert all currently unused parameters */
3318 for (j = 0, firstParmUsed = False; j < request->numObjects; j++)
3320 /* Used or empty objects are skipped */
3321 if ((*paramUsed)[j] == False)
3323 if (IS_FILE_OBJ(request->objects[j].mask) &&
3324 request->objects[j].u.file.origFilename)
3326 if (!InsertArgumentString(w, &compiledMsg,
3328 request, request->objects+j,
3329 segment->mask, relPathHost, relPathDir,
3330 firstParmUsed, processingMask))
3332 XtFree(compiledMsg);
3335 firstParmUsed = True;
3339 * Since we use tmp files for buffers and do not support
3340 * strings; we need not add special code for buffer and
3343 /* fdt: add support for buffers and strings
3344 * else if (IS_BUFFER_OBJ(request->objects[i].mask) &&
3345 * request->objects[i].u.buffer.buffer)
3346 * else if (IS_STRING_OBJ(request->objects[i].mask) &&
3347 * request->objects[i].u.string.string)
3352 else if (segment->argNum <= request->numObjects)
3354 if (IS_FILE_OBJ(request->objects[segment->argNum-1].mask) &&
3355 request->objects[segment->argNum-1].u.file.origFilename)
3357 /* Replace only with the specified argument */
3358 (*paramUsed)[segment->argNum-1] = True;
3360 * All buffer objects have been written to tmp files.
3361 * This code replaces a reference to an object with its
3363 * Tooltalk processing code elsewhere
3364 * (ActionTt.c) detects the conditions under which a buffer
3365 * object reference should be replaced by the buffer contents
3366 * instead of the tmp file name. (i.e. a value field with a
3367 * single argument reference with no additional text). In such
3368 * cases the compiled message string will be ignored.
3370 if (!InsertArgumentString(w, &compiledMsg, &compiledMsgSize,
3371 request, request->objects + segment->argNum - 1,
3372 segment->mask, relPathHost, relPathDir, False,
3375 XtFree(compiledMsg);
3381 * Since we use tmp files for buffers and do not support
3382 * strings; we need not add special code for buffer and
3385 /* fdt: add support for buffers and strings
3386 * else if (IS_BUFFER_OBJ(request->objects[i].mask) &&
3387 * request->objects[i].u.buffer.buffer)
3388 * else if (IS_STRING_OBJ(request->objects[i].mask) &&
3389 * request->objects[i].u.string.string)
3397 if ((piece->compiledMessage = compiledMsg) == NULL)
3400 piece->msgLen = compiledMsg ? strlen(compiledMsg) + 1: 0;
3405 * Given an object, add it to the end of the message buffer. The
3406 * object may refer to a file, thus possibly requiring that it be
3407 * converted to another format.
3411 InsertArgumentString(
3415 ActionRequest *request,
3420 Boolean addLeadingSpace,
3421 unsigned long processingMask )
3432 if (processingMask & _DTAct_TT_VTYPE)
3433 SET_TREAT_AS_FILE(mask);
3435 if (IS_TREAT_AS_FILE(mask))
3437 if (object->type == -1)
3439 /* Object still needs to be typed */
3440 if (IS_FILE_OBJ(object->mask))
3442 char * origInfo = object->u.file.origFilename;
3444 ParseFileArgument(w, request, object, NULL, origInfo, NULL, True);
3449 * Since we use tmp files for buffers and do not support
3450 * strings; we need not add special code for buffer and
3453 /* fdt: add support for buffers and strings
3454 * else if (IS_BUFFER_OBJ(object->mask))
3455 * else if (IS_STRING_OBJ(object->mask))
3459 if (IS_FILE_OBJ(object->mask))
3461 if (processingMask & _DTAct_TT_VTYPE)
3464 * Instead of inserting the object referred to by "Arg_n",
3465 * we need to instead insert the MEDIA attribute for the
3466 * object. If the MEDIA attribute is not defined for the
3467 * datatype associated with this object, then use the
3468 * datatype name itself. If the thing can't be defined, then
3471 if (object->type != (-1))
3473 dataType = (char *)_DtDtsMMBosonToString(object->type);
3475 if ((path = _DtActMapFileName(
3476 request->hostNames[object->u.file.hostIndex],
3477 request->dirNames[object->u.file.dirIndex],
3478 object->u.file.baseFilename,
3484 mediaAttr = DtDtsDataTypeToAttributeValue(dataType, "MEDIA",
3490 value = XtNewString(mediaAttr);
3491 DtDtsFreeAttributeValue(mediaAttr);
3494 value = XtNewString(dataType);
3496 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(value) + 1);
3497 if (addLeadingSpace)
3498 strcat(*bufPtr, " ");
3499 strcat(*bufPtr, value);
3505 if (IS_CMD(request->clonedAction->mask))
3507 /* Map into a real path, relative to the execution host */
3508 if ((path = _DtActMapFileName(
3509 request->hostNames[object->u.file.hostIndex],
3510 request->dirNames[object->u.file.dirIndex],
3511 object->u.file.baseFilename,
3512 request->currentHost)) == NULL)
3514 AddFailedHostToList(request, request->currentHost);
3518 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(path) + 1);
3519 if (addLeadingSpace)
3520 (void)strcat(*bufPtr, " ");
3521 strcat(*bufPtr, path);
3524 else if (IS_TT_MSG(request->clonedAction->mask))
3527 * ToolTalk automatically translates the 'filename' field within
3528 * a message, and expects the incoming name to be relative to
3529 * the local host. So ... we simply need to map the name to
3530 * be relative to the local host. However, if this is not the
3531 * filename, but is instead one of the 'args', then we must
3532 * insert it in a 'neutral' form.
3534 if (processingMask & _DTAct_TT_ARG)
3536 /* Map into "host:/path" */
3537 /* fdt: May need to instead map into 'network indep' form */
3538 InsertUnmappedArgumentString(bufPtr, bufSizePtr, object,
3543 if ((path = _DtActMapFileName(
3544 request->hostNames[object->u.file.hostIndex],
3545 request->dirNames[object->u.file.dirIndex],
3546 object->u.file.baseFilename, NULL)) == NULL)
3551 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, strlen(path) + 1);
3552 if (addLeadingSpace)
3553 (void)strcat(*bufPtr, " ");
3554 strcat(*bufPtr, path);
3561 * Since we use tmp files for buffers and do not support
3562 * strings; we need not add special code for buffer and
3565 /* fdt: add support for buffers and strings
3566 * else if (IS_BUFFER_OBJ(object->mask))
3567 * else if (IS_STRING_OBJ(object->mask))
3571 InsertUnmappedArgumentString(bufPtr, bufSizePtr, object, addLeadingSpace);
3577 * This function knows how to insert a string in "host:/path" format;
3578 * this is essentually an 'unmapped' filename. File arguments which
3579 * have been preceded by the "(String)" qualifier will be saved in
3580 * this fashion. Likewise, any filenames (either in "String" or "File"
3581 * form) for an message will be saved in this format, due to the
3582 * fact that we don't know the execution host, and thus cannot properly
3583 * map the filename using the ToolTalk filename mapping functions.
3587 InsertUnmappedArgumentString(
3591 Boolean addLeadingSpace )
3597 /* No mapping is necessary here. */
3598 if (IS_FILE_OBJ(object->mask))
3600 size = strlen(object->u.file.origFilename) + 4;
3601 *bufPtr = GrowMsgBuffer(*bufPtr, bufSizePtr, size);
3602 if (addLeadingSpace)
3603 (void)strcat(*bufPtr, " ");
3604 strcat(*bufPtr, object->u.file.origFilename);
3608 * Since we use tmp files for buffers and do not support
3609 * strings; we need not add special code for buffer and
3612 /* fdt: add support for buffers and strings
3613 * else if (IS_BUFFER_OBJ(object->mask))
3614 * else if (IS_STRING_OBJ(object->mask))
3620 * This function checks to see if the message buffer is large enough
3621 * to hold the current contents + 'count' more bytes. If it is not
3622 * large enough, then the buffer will be grown. The buffer MUST BE
3633 int currentBufUsed = buffer ? strlen(buffer) : 0;
3635 if ((currentBufUsed + count + 1) >= *size)
3637 (*size) += (count+1 > 1024) ? count + 1 : 1024;
3638 buffer = (char *)XtRealloc(buffer, (Cardinal)*size);
3640 /* If this is the first alloc for the buffer, then terminate the buffer */
3641 if(currentBufUsed == 0)
3650 /***************************************************************************/
3651 /***************************************************************************/
3652 /* Functions For Matching Arguments To A Message String */
3653 /***************************************************************************/
3654 /***************************************************************************/
3658 * If the specified prompt has not already been added to the array of
3659 * prompt strings, then add it. The exception is for stand-alone
3660 * prompt strings, which always are added.
3668 PromptEntry **prompts )
3674 * Standard arguments only want their prompts entered once.
3675 * Stand-alone prompts all have argNum == NO_ARG, and each one
3676 * must be saved. It's a special case.
3678 if (argNum != NO_ARG)
3680 for (i = 0; i < *numPrompts; i++)
3682 if ((*prompts)[i].argIndex == argNum)
3688 *prompts = (PromptEntry *)XtRealloc((char *)*prompts,
3689 (Cardinal)(sizeof(PromptEntry) * *numPrompts));
3690 (*prompts)[(*numPrompts) - 1].argIndex = argNum;
3691 (*prompts)[(*numPrompts) - 1].prompt = prompt;
3696 * This function takes an action DB entry and an action request, and
3697 * determines if enough information was supplied to create the message
3698 * needed to get the work done. If information was missing, then this
3699 * function will return an array of prompt strings, which can be used
3700 * to create a dialog for collecting the missing information. This
3701 * function also returns an indication of how many of the parameters
3704 * The caller is responsible for freeing up the prompt array, but the
3705 * entries in the array MUST NOT be freed up.
3709 MatchParamsToAction(
3710 ActionRequest *request,
3712 PromptEntry **prompts )
3714 Boolean * paramUsed = NULL;
3716 Boolean argsOptionFound;
3718 int lastArgReferenced;
3719 ActionPtr action = request->clonedAction;
3721 /* Initialize things */
3724 argsOptionFound = False;
3725 lastArgReferenced = -1;
3728 * This array lets us know which parameters can be used when we
3729 * encounter the %Args% keyword.
3731 unused = request->numObjects;
3733 paramUsed = (Boolean *)XtMalloc((Cardinal)(sizeof(Boolean) * unused));
3734 for (i = 0; i < unused; i++)
3735 paramUsed[i] = False;
3738 if (IS_CMD(action->mask))
3741 * NOTE: The current implementation of prompt strings requires that
3742 * the segments be evaluated in the same order in which the
3743 * message fields were parsed.
3744 * (See ResolveCommandInvokerMessagePieces() )
3745 * This order is currently "execHost", "execString" and
3746 * "termOpts". This situation arises because
3747 * the existing prompt data structures do NOT identify the
3748 * location of the prompt and hence where to put the
3749 * user-supplied value; except by order of occurrence.
3751 ProcessOneSegment(request, &(action->u.cmd.execHosts), prompts,
3752 numPrompts, &argsOptionFound, &lastArgReferenced,
3753 &unused, paramUsed);
3754 ProcessOneSegment(request, &(action->u.cmd.execString), prompts,
3755 numPrompts, &argsOptionFound, &lastArgReferenced,
3756 &unused, paramUsed);
3757 ProcessOneSegment(request, &(action->u.cmd.termOpts), prompts,
3758 numPrompts, &argsOptionFound, &lastArgReferenced,
3759 &unused, paramUsed);
3761 else if (IS_TT_MSG(action->mask))
3763 ProcessOneSegment(request, &(action->u.tt_msg.tt_op), prompts,
3764 numPrompts, &argsOptionFound, &lastArgReferenced,
3765 &unused, paramUsed);
3766 ProcessOneSegment(request, &(action->u.tt_msg.tt_file), prompts,
3767 numPrompts, &argsOptionFound, &lastArgReferenced,
3768 &unused, paramUsed);
3770 for (i = 0; i < action->u.tt_msg.vtype_count; i++)
3772 ProcessOneSegment(request, &(action->u.tt_msg.tt_argn_vtype[i]),
3773 prompts, numPrompts, &argsOptionFound,
3774 &lastArgReferenced, &unused, paramUsed);
3777 for (i = 0; i < action->u.tt_msg.value_count; i++)
3780 * We require that at most ONE argument be consumed by a
3781 * tt_argn_value field.
3783 ProcessOneSegment(request, &(action->u.tt_msg.tt_argn_value[i]),
3784 prompts, numPrompts, &argsOptionFound,
3785 &lastArgReferenced, &unused, paramUsed);
3791 * Now that we have processed all of the pieces which will ultimately
3792 * used to construct our message, determine if any of the arguments
3793 * passed to _DtActionInvoke were not used; this allows us to tell
3794 * the user that there were unused arguments, so they can choose
3795 * to continue or abort the request.
3796 * If we ever encountered a %Args% keyword, then ultimately all of
3797 * the parameters will be used.
3799 if (argsOptionFound)
3804 * Determine how many arguments were actually unused; only count
3805 * those arguments AFTER the last referenced one. i.e. if arg2
3806 * is referenced, but arg1 and arg3 are not, then only count arg3
3807 * as an unused (and thus extra) parameter.
3809 for (i = 0; ((i < lastArgReferenced - 1) && (i < request->numObjects));
3816 /* This should never happen, but ... */
3821 if (paramUsed) XtFree(paramUsed);
3829 ActionRequest * request,
3831 PromptEntry **prompts,
3833 Boolean * argsOptionFound,
3834 int * lastArgReferenced,
3836 Boolean * paramUsed )
3838 MsgComponent * piece;
3842 * Check each piece of this message component, to see if the parameter
3843 * it expects has been supplied. If the parameter is missing, and
3844 * a prompt was supplied, then add the prompt to the prompt array.
3846 for (i = 0; i < msg->numMsgParts; i++)
3848 piece = msg->parsedMessage + i;
3851 * We only care about %Args% and %Arg_<n>% keywords, and
3852 * entries which have no keyword, but do have a prompt.
3854 if (piece->keyword == ARG)
3856 if (piece->argNum == ALL_ARGS)
3859 * When a %Args% keyword is found, this implies that there
3860 * will ultimately be no unused parameters, because this
3861 * keyword is replaced by all unused parameters.
3863 *argsOptionFound = True;
3865 else if (piece->argNum > 0)
3867 /* Keep track of the largest arg index referenced */
3868 if (piece->argNum > *lastArgReferenced)
3869 *lastArgReferenced = piece->argNum;
3871 /* See if a parameter was supplied for this argNum */
3872 if (piece->argNum > request->numObjects)
3874 /* Parameter is missing; see if a prompt was given */
3876 AddPrompt(piece->argNum, piece->prompt, numPrompts, prompts);
3880 /* Mark this parameter as having been used */
3881 if (!paramUsed[piece->argNum - 1])
3883 paramUsed[piece->argNum - 1] = True;
3889 else if ((piece->keyword == NO_KEYWORD) && (piece->prompt))
3891 /* Entries may be nothing but a prompt */
3892 AddPrompt(NO_ARG, piece->prompt, numPrompts, prompts);
3897 /***************************************************************************/
3898 /***************************************************************************/
3899 /* Prompt Dialog Support */
3900 /***************************************************************************/
3901 /***************************************************************************/
3905 * This is the event handler which catches the 'escape' key when typed
3906 * into the prompt. It will unpost the dialog.
3914 XtPointer num_params)
3919 /* Get the cancel button widget id */
3920 XtSetArg(args[0], XmNuserData, &cancel);
3921 XtGetValues(w, args, 1);
3923 /* Unpost the text annotation dialog */
3924 XtCallCallbacks(cancel, XmNactivateCallback, NULL);
3929 * 'Cancel' callback for the dialog used to collect missing parameters
3930 * from the user. It will free up the memory holding the cancelled
3931 * request and will destroy the dialog.
3937 PromptDialog *dialog,
3938 XtPointer call_data )
3941 unsigned long evalStatus;
3942 unsigned long userStatus;
3943 _DtActInvRecT *invp;
3945 /* Destroy the dialog */
3946 XtDestroyWidget(XtParent(dialog->topLevel));
3948 /* Free up the prompt sub-structure */
3949 XtFree((char *)dialog->prompts);
3952 invp = _DtActFindInvRec(dialog->request->invocId);
3953 myassert(invp); /* There should always be an invocation record */
3955 /* Free up the original request structure */
3956 _DtFreeRequest(dialog->request);
3958 /* Free up the callback structure */
3959 XtFree((char *)dialog);
3962 return; /* This should never happen */
3964 SET_INV_CANCEL(invp->state);
3967 * Evaluate whether we are done with this invocation -- are there
3968 * uncompleted children? There should not be any subsequent invocations
3969 * to worry about since this cancel effectively aborts further processing.
3971 * We may have to return values to the caller.
3974 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
3979 * This function changes the focus from the given "widget's"
3980 * tab group to the next tab group.
3984 ChangePromptTraversal(
3986 PromptDialog *dialog,
3987 XtPointer call_data )
3989 XmProcessTraversal (widget, XmTRAVERSE_NEXT_TAB_GROUP);
3993 * 'Ok' callback for the dialog used to collect missing parameters
3994 * from the user. It will redo the array of parameter strings, and
3995 * then execute the command, given whatever the user has supplied.
3996 * It will also destroy the dialog box.
4000 ProcessPromptDialog(
4002 PromptDialog *dialog,
4003 XtPointer call_data )
4009 /* Unpost the dialog */
4010 XtUnmanageChild(dialog->topLevel);
4013 * Given the set of strings supplied by the user, update the
4014 * object array which is part of the original request.
4016 for (i = 0; i < dialog->numPrompts; i++)
4018 value = XmTextFieldGetString(dialog->prompts[i].promptWidget);
4020 /* Do we need to grow the object array? */
4021 if (dialog->prompts[i].argIndex > 0)
4023 if (_DtEmptyString(value))
4029 if (dialog->prompts[i].argIndex > dialog->request->numObjects)
4031 dialog->request->objects = (ObjectData *)
4032 XtRealloc((char *)dialog->request->objects,
4033 (Cardinal)(sizeof(ObjectData) * (dialog->prompts[i].argIndex)));
4035 /* Initialize the new array entries */
4036 for (j = dialog->request->numObjects;
4037 j < dialog->prompts[i].argIndex;
4040 dialog->request->objects[j].mask = 0;
4041 SET_FILE_OBJ(dialog->request->objects[j].mask);
4042 dialog->request->objects[j].type = -1;
4043 dialog->request->objects[j].u.file.hostIndex = -1;
4044 dialog->request->objects[j].u.file.dirIndex = -1;
4045 dialog->request->objects[j].u.file.origFilename = NULL;
4046 dialog->request->objects[j].u.file.origHostname = NULL;
4047 dialog->request->objects[j].u.file.baseFilename = NULL;
4048 dialog->request->objects[j].u.file.bp = 0;
4049 dialog->request->objects[j].u.file.sizebp = 0;
4050 dialog->request->objects[j].u.buffer.bp = 0;
4051 dialog->request->objects[j].u.buffer.size = 0;
4052 dialog->request->objects[j].u.string.string = 0;
4053 SET_UNKNOWN_IF_DIR(dialog->request->objects[j].mask);
4055 dialog->request->numObjects = dialog->prompts[i].argIndex;
4058 * These values cannot be broken up into host/dir/file components,
4059 * nor can they be typed, until we know it they refer to a file.
4060 * This can't be determined until we construct the action message.
4062 dialog->request->objects[dialog->prompts[i].argIndex-1].u.file.
4063 origFilename = value;
4065 else /* Prompt-only input */
4068 * Prompt-only input can't fit in our ordered object array,
4069 * since they don't have a unique argIndex which can be used
4070 * as the index into the object array.
4072 dialog->request->numPromptInputs++;
4073 dialog->request->promptInputs = (String *)
4074 XtRealloc((char *)dialog->request->promptInputs,
4075 (Cardinal)(dialog->request->numPromptInputs * sizeof(String)));
4077 dialog->request->promptInputs[dialog->request->numPromptInputs-1] =
4082 /* Destroy the dialog */
4083 XtDestroyWidget(XtParent(dialog->topLevel));
4084 XmUpdateDisplay(widget);
4087 * Invoke the action using the information we've collected.
4088 * If this was a single argument action, then the reprocessing
4089 * of it may have generated another dialog, so we can only free
4090 * up the request, when all processing is done.
4092 if (ProcessRequest(dialog->associatedWidget, dialog->request))
4094 _DtActInvRecT *invp;
4095 unsigned long evalStatus;
4096 unsigned long userStatus;
4098 if ( (invp = _DtActFindInvRec(dialog->request->invocId)) )
4100 /* all done invoking ? */
4101 RESET_INV_PENDING(invp->state);
4103 /* We should only get here if all requests have been honored */
4104 SET_INV_COMPLETE(invp->state);
4107 * evaluate whether all child actions have been completed
4108 * and if its time to call the user callback.
4110 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
4112 myassert(invp); /* there should always be one to find */
4113 _DtFreeRequest(dialog->request);
4116 /* Free up the prompt sub-structure */
4117 XtFree((char *)dialog->prompts);
4119 /* Free up the callback structure */
4120 XtFree((char *)dialog);
4125 * This function takes the array of prompt strings, and creates a
4126 * dialog box, using these prompt strings as the labels for a set
4133 ActionRequest *request,
4135 PromptEntry *prompts )
4138 PromptDialog * dialog;
4139 DialogPromptEntry * promptDes;
4142 Widget shell, bboard, frame, form, label;
4143 Widget promptLabel, topAttach;
4144 Widget separator, ok, cancel;
4148 XmString labelString;
4149 XWindowAttributes xwa;
4151 Boolean is_mapped = False;
4152 static XtTranslations trans_table;
4153 static Boolean first = True;
4154 Atom xa_WM_DELETE_WINDOW;
4157 * Want to set up the Escape key so that it will unpost the dialog.
4159 _DtSvcProcessLock();
4162 XtAppAddActions(XtWidgetToApplicationContext(w), actionTable, 1);
4163 trans_table = XtParseTranslationTable(translations_escape);
4166 _DtSvcProcessUnlock();
4168 /* Allocate the structures we'll be needing */
4169 dialog = (PromptDialog *)XtMalloc((Cardinal)sizeof(PromptDialog));
4170 promptDes = (DialogPromptEntry *)XtMalloc((Cardinal)
4171 (sizeof(DialogPromptEntry) * numPrompts));
4174 /* Create the shell, frame and form used for the dialog. */
4176 title = (char *)XtMalloc((Cardinal)
4177 (strlen(PromptDialogTitle)+ strlen(request->clonedAction->label) + 1));
4178 (void)sprintf(title, "%1$s%2$s", PromptDialogTitle, request->clonedAction->label);
4180 XtSetArg (args[n], XmNallowShellResize, True); n++;
4181 XtSetArg (args[n], XmNtitle, title); n++;
4182 shell = XmCreateDialogShell (w, "promptDialog", args, n);
4185 if (XtIsRealized(w))
4187 status = XGetWindowAttributes (XtDisplay (w), XtWindow (w), &xwa);
4188 if (status && (xwa.map_state == IsViewable))
4193 XtSetArg (args[n], XmNmarginWidth, 0); n++;
4194 XtSetArg (args[n], XmNmarginHeight, 0); n++;
4197 XtSetArg (args[n], XmNdefaultPosition, False);
4200 bboard = XmCreateBulletinBoard (shell, "bboard", args, n);
4203 XtSetArg (args[n], XmNshadowThickness, 1); n++;
4204 XtSetArg (args[n], XmNshadowType, XmSHADOW_OUT); n++;
4205 frame = XmCreateFrame (bboard, "frame", args, n);
4206 XtManageChild (frame);
4209 XtSetArg (args[n], XmNautoUnmanage, False); n++;
4210 XtSetArg (args[n], XmNtextTranslations, trans_table); n++;
4211 form = XmCreateForm (frame, "form", args, n);
4212 XtManageChild (form);
4214 /* Create the dialog description label */
4216 pt1 = XmStringCreateLocalized(PromptDialogLabel);
4218 XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
4219 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
4220 XtSetArg(args[n], XmNleftOffset, 20); n++;
4221 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
4222 XtSetArg(args[n], XmNrightOffset, 20); n++;
4223 XtSetArg(args[n], XmNtopOffset, 15); n++;
4224 XtSetArg(args[n], XmNlabelString, pt1); n++;
4225 label = XmCreateLabelGadget(form, "label", args, n);
4226 XtManageChild (label);
4229 /* Create each of the needed prompts */
4231 for (count = 0; count < numPrompts; count++)
4233 promptDes[count].argIndex = prompts[count].argIndex;
4235 pt1 = XmStringCreateLocalized(prompts[count].prompt);
4237 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4238 XtSetArg(args[n], XmNtopWidget, topAttach); n++;
4239 XtSetArg(args[n], XmNtopOffset, 10); n++;
4240 XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
4241 XtSetArg(args[n], XmNleftOffset, 30); n++;
4242 XtSetArg(args[n], XmNlabelString, pt1); n++;
4243 promptLabel = XmCreateLabelGadget(form, "promptLabel", args, n);
4244 XtManageChild(promptLabel);
4248 XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4249 XtSetArg(args[n], XmNtopWidget, topAttach); n++;
4250 XtSetArg(args[n], XmNtopOffset, 8); n++;
4251 XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
4252 XtSetArg(args[n], XmNrightOffset, 30); n++;
4253 XtSetArg(args[n], XmNtraversalOn, True); n++;
4254 XtSetArg(args[n], XmNleftAttachment,XmATTACH_WIDGET ); n++;
4255 XtSetArg(args[n], XmNleftWidget, promptLabel); n++;
4256 XtSetArg(args[n], XmNleftOffset, 15); n++;
4257 promptDes[count].promptWidget = XmCreateTextField(form, "text", args, n);
4259 XtManageChild(promptDes[count].promptWidget);
4261 XmAddTabGroup(promptDes[count].promptWidget);
4262 topAttach = promptDes[count].promptWidget;
4266 /* Create a separator between the buttons */
4269 XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
4270 XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
4271 XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4272 XtSetArg (args[n], XmNtopWidget, topAttach); n++;
4273 XtSetArg (args[n], XmNtopOffset, 20); n++;
4274 separator = XmCreateSeparatorGadget (form, "separator", args, n);
4275 XtManageChild (separator);
4278 /* Create the ok and cancel buttons */
4281 labelString = XmStringCreateLocalized((String)_DtOkString);
4282 XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
4283 XtSetArg (args[n], XmNleftPosition, 5 + 10); n++;
4284 XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
4285 XtSetArg (args[n], XmNrightPosition, 31 + 10); n++;
4286 XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4287 XtSetArg (args[n], XmNtopWidget, separator); n++;
4288 XtSetArg (args[n], XmNtopOffset, 16); n++;
4289 XtSetArg (args[n], XmNbottomOffset, 16); n++;
4290 XtSetArg (args[n], XmNmarginHeight, 4); n++;
4291 XtSetArg (args[n], XmNshowAsDefault, True); n++;
4292 XtSetArg (args[n], XmNlabelString, labelString); n++;
4293 ok = XmCreatePushButtonGadget (form, "ok", args, n);
4295 XtAddCallback(ok, XmNactivateCallback, (XtCallbackProc)ProcessPromptDialog,
4297 XmStringFree(labelString);
4299 /* Set the default action */
4301 if (numPrompts <= 1)
4304 XtSetArg (args[n], XmNdefaultButton, ok); n++;
4305 XtSetValues(bboard, args, n);
4311 * Want to set the traversal so that if "return" is hit in the
4312 * last prompt, the "ProcessPromptDialog" callback is invoked.
4313 * Otherwise, the "return" should move the focus to the next
4314 * which is in a different (tab group).
4316 for (i = 0; i < numPrompts; i++)
4318 if (i <= (numPrompts - 2))
4319 XtAddCallback(promptDes[i].promptWidget, XmNactivateCallback,
4320 (XtCallbackProc)ChangePromptTraversal,
4323 XtAddCallback(promptDes[i].promptWidget, XmNactivateCallback,
4324 (XtCallbackProc)ProcessPromptDialog,
4330 labelString = XmStringCreateLocalized((String)_DtCancelString);
4331 XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
4332 XtSetArg (args[n], XmNleftPosition, 37 + 22); n++;
4333 XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
4334 XtSetArg (args[n], XmNrightPosition, 63 + 22); n++;
4335 XtSetArg (args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
4336 XtSetArg (args[n], XmNtopWidget, separator); n++;
4337 XtSetArg (args[n], XmNtopOffset, 21); n++;
4338 XtSetArg (args[n], XmNbottomOffset, 21); n++;
4339 XtSetArg (args[n], XmNmarginHeight, 4); n++;
4340 XtSetArg (args[n], XmNlabelString, labelString); n++;
4341 cancel = XmCreatePushButtonGadget (form, "cancel", args, n);
4342 XtManageChild(cancel);
4343 XtAddCallback(cancel, XmNactivateCallback, (XtCallbackProc)CancelPromptDialog,
4345 XmStringFree(labelString);
4348 * For each prompt, must set up the Escape key to be equivalent
4349 * to the "Cancel button.
4351 for (i = 0; i < numPrompts; i++) {
4353 XtSetArg(args[n], XmNuserData, cancel); n++;
4354 XtSetValues(promptDes[i].promptWidget, args, n);
4358 * If the widget is not mapped, center this dialog.
4362 Dimension dialogWd, dialogHt;
4364 XtSetArg(args[0], XmNmappedWhenManaged, False);
4365 XtSetValues(shell, args, 1);
4367 XtManageChild(bboard);
4368 XtRealizeWidget(shell);
4370 XtSetArg(args[0], XmNwidth, &dialogWd);
4371 XtSetArg(args[1], XmNheight, &dialogHt);
4372 XtGetValues(bboard, args, 2);
4374 XtSetArg (args[0], XmNx,
4375 (WidthOfScreen(XtScreen(bboard)) - dialogWd) / 2U);
4376 XtSetArg (args[1], XmNy,
4377 (HeightOfScreen(XtScreen(bboard)) - dialogHt) / 2U);
4378 XtSetValues (bboard, args, 2);
4381 /* Adjust the decorations for the dialog shell of the dialog */
4384 XtSetArg (args[n], XmNmwmDecorations,
4385 MWM_DECOR_BORDER | MWM_DECOR_MENU | MWM_DECOR_TITLE); n++;
4386 XtSetValues(shell, args, n);
4388 xa_WM_DELETE_WINDOW =
4389 XInternAtom(XtDisplay(shell), "WM_DELETE_WINDOW", False);
4390 XmAddWMProtocolCallback(
4391 shell, xa_WM_DELETE_WINDOW,
4392 (XtCallbackProc) CancelPromptDialog, (XtPointer) dialog);
4394 /* Fill in our instance structure */
4395 dialog->request = request;
4396 dialog->topLevel = bboard;
4397 dialog->numPrompts = count;
4398 dialog->prompts = promptDes;
4399 dialog->associatedWidget = w;
4401 /* Post the dialog */
4402 XtSetArg(args[0], XmNmappedWhenManaged, True);
4403 XtSetValues(shell, args, 1);
4404 XtManageChild(bboard);
4406 /* Make the first prompt automatically get the focus. */
4407 if (numPrompts >= 0)
4408 XmProcessTraversal(promptDes[0].promptWidget, XmTRAVERSE_CURRENT);
4412 /***************************************************************************/
4413 /***************************************************************************/
4414 /* Continue Dialog Support */
4415 /***************************************************************************/
4416 /***************************************************************************/
4420 * 'Ok' callback for the abort/continue dialog. It will continue with the
4421 * processing of the request, ignoring any unused parameters.
4427 XtPointer user_data,
4428 XtPointer call_data )
4432 ContinueDialog *dialog = (ContinueDialog *)user_data;
4434 /* Destroy the dialog */
4435 XtDestroyWidget(XtParent(dialog->topLevel));
4436 XmUpdateDisplay(widget);
4439 * If we need to collect some prompt input from the user, then
4440 * post the prompt dialog; otherwise, send the action request.
4442 if (dialog->numPrompts == 0)
4444 if (ProcessRequest(dialog->associatedWidget, dialog->request))
4446 _DtActInvRecT *invp;
4448 if((invp=_DtActFindInvRec(dialog->request->invocId))!=NULL)
4450 /* all done invoking ? */
4451 RESET_INV_PENDING(invp->state);
4453 /* We should only get here if all requests have been honored */
4454 SET_INV_COMPLETE(invp->state);
4457 * evaluate whether all child actions have been completed
4458 * and if its time to call the user callback.
4460 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
4463 myassert(invp); /* there should always be one to find */
4464 _DtFreeRequest(dialog->request);
4469 CreatePromptDialog(dialog->associatedWidget, dialog->request,
4475 /* Free up the prompt sub-structure */
4476 for (i = 0; i < dialog->numPrompts; i++)
4477 XtFree(dialog->prompts[i].prompt);
4478 XtFree((char *)dialog->prompts);
4480 /* Free up the callback structure */
4481 XtFree((char *)dialog);
4486 * 'Cancel' callback for the dialog which prompts the user to continue
4487 * or abort, when too many parameters have been supplied. This will
4488 * free up the dialog data and the request and will destroy the dialog.
4494 XtPointer user_data,
4495 XtPointer call_data )
4499 ContinueDialog *dialog = (ContinueDialog *)user_data;
4500 unsigned long evalStatus;
4501 unsigned long userStatus;
4502 _DtActInvRecT *invp;
4504 /* Destroy the dialog */
4505 XtDestroyWidget(XtParent(dialog->topLevel));
4507 /* Free up the prompt sub-structure */
4508 for (i = 0; i < dialog->numPrompts; i++)
4509 XtFree(dialog->prompts[i].prompt);
4510 XtFree((char *)dialog->prompts);
4512 /* get the invocation record */
4513 invp = _DtActFindInvRec(dialog->request->invocId);
4514 myassert(invp); /* There should always be one available */
4516 /* Free up the original request structure */
4517 _DtFreeRequest(dialog->request);
4519 /* Free up the callback structure */
4520 XtFree((char *)dialog);
4523 return; /* should never happen */
4525 SET_INV_CANCEL(invp->state);
4527 * Evaluate whether we are done with this invocation -- are there
4528 * uncompleted children? There should not be any subsequent invocations
4529 * to worry about since this cancel effectively aborts further processing.
4531 * We may have to return values to the caller.
4533 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
4537 * When an action is requested, and more parameters than are needed
4538 * are supplied, the user will be prompted to continue with the
4539 * operation (ignoring the extra parameters), or to abort the request.
4541 * This function builds the dialog which will collect the user's response.
4545 CreateContinueDialog(
4547 ActionRequest *request,
4549 PromptEntry *prompts )
4552 ContinueDialog * dialog;
4558 XmString ok, cancel;
4561 /* Allocate the structures we'll be needing */
4562 dialog = (ContinueDialog *)XtMalloc((Cardinal)sizeof(ContinueDialog));
4563 dialog->request = request;
4564 dialog->associatedWidget = w;
4565 dialog->numPrompts = numPrompts;
4568 * We need to make a clone of the prompt array, since the strings
4569 * it contains are not ones we can guarantee will be around when
4570 * the user finally responds to this dialog.
4574 dialog->prompts = (PromptEntry *)
4575 XtMalloc((Cardinal)(sizeof(PromptEntry) * numPrompts));
4576 for (i = 0; i < numPrompts; i++)
4578 dialog->prompts[i].argIndex = prompts[i].argIndex;
4579 dialog->prompts[i].prompt = XtNewString(prompts[i].prompt);
4583 dialog->prompts = NULL;
4585 ok = XmStringCreateLocalized((String)_DtOkString);
4586 cancel = XmStringCreateLocalized((String)_DtCancelString);
4588 /* Create the error dialog */
4589 fmt = XtNewString((char *)Dt11GETMESSAGE(2, 2, "%1$s%2$s"));
4590 title = (char *)XtMalloc((Cardinal)
4591 (strlen(PromptDialogTitle) +
4592 strlen(request->clonedAction->label) +
4594 (void)sprintf(title, fmt, PromptDialogTitle, request->clonedAction->label);
4595 label = XmStringCreateLocalized(ContinueMessage);
4599 XtSetArg(args[n], XmNmessageString, label); n++;
4600 XtSetArg(args[n], XmNtitle, title); n++;
4601 XtSetArg(args[n], XmNokLabelString, ok); n++;
4602 XtSetArg(args[n], XmNcancelLabelString, cancel); n++;
4603 dialog->topLevel = XmCreateWarningDialog(w, "continueDialog", args, n);
4606 XmStringFree(cancel);
4607 XmStringFree(label);
4609 XtUnmanageChild(XmMessageBoxGetChild(dialog->topLevel,
4610 XmDIALOG_HELP_BUTTON));
4611 XtAddCallback(dialog->topLevel, XmNokCallback, ContinueRequest,
4613 XtAddCallback(dialog->topLevel, XmNcancelCallback, CancelRequest,
4615 XtManageChild(dialog->topLevel);
4619 /***************************************************************************/
4620 /***************************************************************************/
4621 /* Command Invoker Specific Functions */
4622 /***************************************************************************/
4623 /***************************************************************************/
4626 * This is the entry point into the command-specific world. All of the code
4627 * before this has been written to handle any of the different transport
4628 * types. The code from this point on will know specifically how to
4629 * interact with the command invoker layer. We will start by taking each
4630 * of the pieces of information making up the command invoker request, and
4631 * resolving any of the keywords, by replacing them with the appropriate
4632 * information. If this fails (which it only should do if we try to map
4633 * a file to a host which cannot be accessed), then we will either continue,
4634 * using the next exec host, or we will terminate, if no more hosts are left.
4638 ProcessCommandInvokerRequest(
4640 ActionRequest *request,
4647 ActionPtr action = request->clonedAction;
4648 _DtActInvRecT *invp = NULL;
4649 _DtActChildRecT *childp = NULL;
4651 if (ResolveCommandInvokerMessagePieces(w, request, relPathHost, relPathDir))
4654 * Issue the request; the success/failure notification comes
4655 * asynchronously; that's when everything gets cleaned up, or
4656 * tried again, for the next exec host.
4658 __ExtractCWD(request, &cwdHost, &cwdDir, True);
4659 InitiateCommandInvokerRequest( w, request, cwdHost, cwdDir);
4665 if ( !(invp = _DtActFindInvRec(request->invocId) ) )
4666 myassert( 0 /* could not find invocation record */ );
4668 if ( !(childp=_DtActFindChildRec(request->invocId,request->childId)))
4669 myassert( 0 /* could not find child record */ );
4672 * The only way we could have reached here is if the execution host
4673 * was not accessible, and we tried to map one of the data files to
4674 * be relative to this host. If there are other hosts to be tried,
4675 * then we will retry the request on the next host; otherwise, we
4676 * will post an error dialog, and bail out.
4679 request->hostIndex++;
4681 if (request->hostIndex >= action->u.cmd.execHostCount)
4683 /* No more hosts to try; report an error, and bail out */
4685 if ( invp && childp )
4687 SET_INV_ERROR(invp->state);
4688 childp->childState = _DtActCHILD_FAILED;
4692 * Cleanup should happen later when we return up the stack.
4695 if (action->u.cmd.execHostCount <= 1)
4697 /* Display error dialog listing just the one failed exec host */
4698 HostAccessError(w, request->clonedAction->label, request->badHostList);
4702 /* Display error dialog listing all failed exec hosts */
4703 MultiHostAccessError(w, request->clonedAction->label, request->badHostList);
4708 if ( invp && childp )
4711 * Delete child record for failed exec on this host
4713 _DtActDeleteChildRec(invp,childp);
4714 SET_INV_PENDING(invp->state);
4715 if ( ! invp->numChildren )
4716 RESET_INV_WORKING(invp->state);
4719 /* Retry the request, using the next exec host */
4720 PrepareAndExecuteAction(w, request);
4728 * This function takes all of the pieces making up a command invoker request,
4729 * and resolves any references to keywords, using both the passed-in
4730 * arguments, and any information collected from the prompt dialog.
4734 ResolveCommandInvokerMessagePieces(
4736 ActionRequest *request,
4741 ActionPtr action = request->clonedAction;
4742 cmdAttr * cmd = &(action->u.cmd);
4744 Boolean * paramUsed = NULL;
4745 int promptDataIndex = 0;
4747 * NOTE: The current implementation of prompt strings requires that
4748 * the segments be evaluated in the same order in which the
4749 * action fields were parsed. (See MatchParamsToAction() )
4750 * This order is currently "execHost", "execString" and
4751 * "termOpts". This situation arises because
4752 * the existing prompt data structures do NOT identify the
4753 * location of the prompt and hence where to put the
4754 * user-supplied value; except by order of occurrence.
4757 /* Set up the next host to execute on */
4758 _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
4759 &(action->u.cmd.execHosts), True, 0, ¶mUsed,
4761 SetExecHost(request);
4763 if ((_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
4764 &(cmd->execString), False, 0, ¶mUsed,
4765 &promptDataIndex) == False) ||
4766 (_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
4767 &(cmd->termOpts), False, 0, ¶mUsed,
4768 &promptDataIndex) == False))
4770 /* Free up any intermediate work we've done here */
4771 XtFree(cmd->execString.compiledMessage);
4772 XtFree(cmd->termOpts.compiledMessage);
4773 XtFree(cmd->execHosts.compiledMessage);
4774 cmd->execString.compiledMessage = NULL;
4775 cmd->termOpts.compiledMessage = NULL;
4776 cmd->execHosts.compiledMessage = NULL;
4782 * If term_opts were passed in to the _DtActionInvoke() function, then
4783 * append them to the term_opts derived from the action definition and
4784 * internal defaults. This should give precedence to the last defined
4787 if ( request->termOpts )
4789 termOpts = XtMalloc( strlen(cmd->termOpts.compiledMessage) +
4790 strlen(request->termOpts) + 2 );
4791 strcpy(termOpts,cmd->termOpts.compiledMessage);
4792 strcat(termOpts," ");
4793 strcat(termOpts,request->termOpts);
4794 XtFree(cmd->termOpts.compiledMessage);
4795 cmd->termOpts.compiledMessage = termOpts;
4804 * Process a command-invoker request.
4808 InitiateCommandInvokerRequest(
4810 ActionRequest *request,
4815 char procIdBuf[_DtAct_MAX_BUF_SIZE];
4816 char tmpFileBuf[_DtAct_MAX_BUF_SIZE];
4817 char *procId; /* for dtexec command line */
4818 char *tmpFiles = NULL; /* for dtexec command line */
4819 _DtActInvRecT *invp;
4820 _DtActChildRecT *childp;
4821 CallbackData *data=(CallbackData *)XtMalloc((Cardinal)sizeof(CallbackData));
4822 ActionPtr action = request->clonedAction;
4824 tmpFileBuf[0]='\0'; /* seed the buffer with a null string */
4827 * Generate the procId option string for dtexec
4830 /* Get the default procId from toolTalk */
4831 switch ( tt_ptr_error(procId = tt_default_procid()) )
4834 ; /* fall through */
4835 case TT_ERR_PROCID: /* Try to establish a connection */
4837 if ( !_DtInitializeToolTalk(NULL) )
4839 else if ( tt_ptr_error(procId = tt_default_procid()) != TT_OK )
4841 myassert( 0 ); /* we should never get here */
4853 * The string generated for procId should never exceed the procId buf size.
4855 sprintf(procIdBuf,"%s_%d_%lu",
4856 _DtActNULL_GUARD(procId),
4857 (int) request->invocId,
4860 myassert( strlen(procIdBuf) < sizeof(procIdBuf) );
4864 procId = procIdBuf; /* no need to malloc */
4867 * Generate string of tmp file args for dtexec.
4870 if ( (invp = _DtActFindInvRec(request->invocId)) != NULL )
4873 _DtActFindChildRec(request->invocId,request->childId)) != NULL )
4879 for(i = 0; i < childp->numObjects; i++)
4882 if(!(IS_BUFFER_OBJ(invp->info[childp->argMap[i].argIdx].mask)))
4883 continue; /* not a buffer object */
4885 if ( !(p = invp->info[childp->argMap[i].argIdx].name) )
4886 continue; /* no tmp file name */
4888 /* Add up the string length of the file name */
4889 if((len += strlen(" -tmp ") + strlen(p)) < sizeof(tmpFileBuf))
4892 * Use the automatic tmpFileBuf if possible
4894 strcat(tmpFileBuf," -tmp ");
4895 strcat(tmpFileBuf,p);
4900 * Malloc more space if necessary
4903 tmpFiles = XtMalloc(len + 1);
4904 strcpy(tmpFiles,tmpFileBuf);
4905 strcpy(tmpFiles," -tmp ");
4910 if ( len > 0 && len < sizeof(tmpFileBuf) )
4911 tmpFiles = tmpFileBuf;
4915 myassert( 0 /* could not find child rec */ );
4922 /* Fill out the callback structure */
4923 data->actionLabel = XtNewString(request->clonedAction->label);
4924 data->associatedWidget = w;
4926 data->actionPtr = action;
4927 data->requestPtr = _DtCloneRequest(request);
4930 if ( _DtActionCommandInvoke(action->mask & _DtAct_WINTYPE_BITS, host, dir,
4931 action->u.cmd.execString.compiledMessage,
4932 action->u.cmd.termOpts.compiledMessage,
4933 request->currentHost,
4936 CmdInvSuccessfulRequest, (XtPointer)data,
4937 CmdInvFailedRequest, (XtPointer)data) )
4939 SET_INV_CMD_QUEUED(invp->state);
4942 if ( tmpFiles != tmpFileBuf )
4948 * Sets the 'currentHost' field within the request structure to
4949 * the name of the next exec host to try. Before any of the exec hosts
4950 * in the action definition are tried, we will try the host passed in as
4951 * part of the request, if one was specified.
4956 ActionRequest * request )
4959 ActionPtr action = request->clonedAction;
4961 int hostListSize = 0;
4962 char ** hostList = NULL;
4964 XtFree(request->currentHost);
4966 /* If this is the first call, we may need to parse the host list */
4967 if (action->u.cmd.execHostArray == NULL)
4969 /* Explicitly specified execHost overrides action definition execHost */
4970 if (request->execHost)
4971 ParseHostList(request->execHost, &hostList, &hostListSize, &hostCount);
4972 else if (action->u.cmd.execHosts.compiledMessage)
4974 ParseHostList(action->u.cmd.execHosts.compiledMessage, &hostList,
4975 &hostListSize, &hostCount);
4978 RemoveDuplicateHostNames(hostList, &hostCount);
4980 action->u.cmd.execHostArray = hostList;
4981 action->u.cmd.execHostCount = hostCount;
4984 if (action->u.cmd.execHostCount == 0)
4987 * Oh boy ... someone is trying to be nasty! The only way we could
4988 * have gotten here was to have the action's 'EXEC_HOST' field set
4989 * to nothing but a prompt string, and then the user left the string
4990 * empty! We'll default the local host, and hope it works.
4993 * fdt: we really should default to whatever has been configured
4994 * as the default execution host, followed by the LocalHost,
4995 * if they are not the same.
4997 request->currentHost = _DtGetLocalHostName();
5001 request->currentHost =
5002 XtNewString(action->u.cmd.execHostArray[request->hostIndex]);
5008 * This function takes a string of comma-separated host names, and adds them
5009 * to the passed-in string array.
5015 char *** hostListPtr,
5016 int * hostListSizePtr,
5017 int * hostCountPtr )
5022 _Xstrtokparams strtok_buf;
5024 workString = XtNewString(hostString);
5025 nextHost = _XStrtok(workString, ",", strtok_buf);
5029 nextHost = _DtStripSpaces(nextHost);
5031 if (strlen(nextHost) > 0)
5033 if (*hostCountPtr >= *hostListSizePtr)
5035 (*hostListSizePtr) += 5;
5036 (*hostListPtr) = (char **)XtRealloc((char *)(*hostListPtr),
5037 sizeof(char *) * (*hostListSizePtr));
5040 (*hostListPtr)[*hostCountPtr] = XtNewString(nextHost);
5044 nextHost = _XStrtok(NULL, ",", strtok_buf);
5052 * This function goes through the compiled list of exec hosts, and removes
5053 * any duplicate entries. It is not very useful to attempt to execute on
5054 * a given host, more than once.
5057 RemoveDuplicateHostNames (
5059 int * hostCountPtr )
5064 for (i = 0; i < *hostCountPtr; i++)
5066 for (j = i+1; j < *hostCountPtr; )
5068 if (strcmp(hostList[i], hostList[j]) == 0)
5070 /* Remove the second entry */
5071 XtFree(hostList[j]);
5072 for (k = j; k < (*hostCountPtr) - 1; k++)
5073 hostList[k] = hostList[k+1];
5084 * When one of the exec hosts fails, we add it to the list of failed
5085 * hostnames, so that if ultimately all of the hosts fail, we have a
5086 * list we can display within the error dialog.
5090 AddFailedHostToList (
5091 ActionRequest * request,
5097 if (request->badHostList)
5098 curLen = strlen(request->badHostList);
5102 request->badHostList = XtRealloc(request->badHostList,
5103 curLen + 10 + strlen(badHost));
5107 strcat(request->badHostList, ", ");
5108 strcat(request->badHostList, badHost);
5111 strcpy(request->badHostList, badHost);
5117 * This callback is invoked when the Command Invoker library has successfully
5118 * exectued an action. We need to free up everything associated with this
5123 CmdInvSuccessfulRequest(
5128 _DtActInvRecT *invp = NULL;
5129 _DtActChildRecT *childrecp = NULL;
5131 CallbackData *data = (CallbackData *) data2;
5134 * Mark this invocation step as done
5135 * The child process itself may not be done.
5137 if ((invp = _DtActFindInvRec(data->requestPtr->invocId)) != NULL )
5139 extern void *_DtCmdCheckQForId(DtActionInvocationID id);
5141 SET_INV_DONE(invp->state);
5142 RESET_INV_CMD_QUEUED(invp->state);
5144 * Are there still more commands queued for this request ?
5146 if ( _DtCmdCheckQForId(invp->id) )
5149 * If so; set the command queued bit
5151 SET_INV_CMD_QUEUED(invp->state);
5155 * This may not be the right place to set the child state for
5156 * command actions. The child process may already have communicated
5157 * its status via TT messaging OR it may already have exited.
5158 * For now we set the state here -- till we find a better place.
5160 if ((childrecp = _DtActFindChildRec(invp->id,data->requestPtr->childId)))
5161 childrecp->childState = _DtActCHILD_ALIVE_UNKNOWN;
5163 myassert(0 /* could not find child record */ );
5165 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
5168 myassert( 0 /* Couldn't find an invocation record */);
5170 _DtFreeRequest(data->requestPtr);
5171 XtFree(data->actionLabel);
5172 XtFree((char *)data);
5178 * This callback is invoked when the Command Invoker library has failed
5179 * to exectue an action. It there are additional execHosts to be processed,
5180 * then try the command again, using the next host. If there are no more
5181 * hosts, then post an error dialog, and give up (freeing all data
5182 * associated with this request).
5186 CmdInvFailedRequest(
5187 char *error_message,
5191 CallbackData * data = (CallbackData *) data2;
5192 String msg = error_message;
5194 ActionRequest * request;
5195 _DtActChildRecT *childp = NULL;
5196 _DtActInvRecT *invp = NULL;
5200 * If this was not the last host in the execHost list, then retry
5201 * the request, using the next host; if this was the last host,
5202 * then we failed, and it is time to post an error dialog. If the
5203 * host list had only one item, then to be backwards compatible,
5204 * we will display the message returned by the command invoker.
5205 * Otherwise, we will simple display the list of execHosts, along
5206 * with a message saying they could not be accessed.
5208 request = data->requestPtr;
5210 if (request->clonedAction)
5211 action = request->clonedAction;
5213 action = data->actionPtr;
5214 request->hostIndex++;
5215 AddFailedHostToList(request, request->currentHost);
5217 if ( !(invp = _DtActFindInvRec(request->invocId) ) )
5218 myassert( 0 /* could not find invocation record */ );
5220 if ( !(childp=_DtActFindChildRec(request->invocId,request->childId)))
5221 myassert( 0 /* could not find child record */ );
5224 * Make sure the CMD_QUEUED bit is set correctly
5228 extern void *_DtCmdCheckQForId(DtActionInvocationID id);
5230 SET_INV_DONE(invp->state);
5231 RESET_INV_CMD_QUEUED(invp->state);
5233 * Are there still more commands queued for this request ?
5235 if ( _DtCmdCheckQForId(invp->id) )
5238 * If so; set the command queued bit
5240 SET_INV_CMD_QUEUED(invp->state);
5244 if (request->hostIndex < action->u.cmd.execHostCount)
5247 * Free up the child structure for the failed command request
5248 * We may be trying again on another host but a new child rec
5249 * will be allocated in PrepareAndExecute().
5252 if ( invp && childp )
5255 * Delete child record for failed exec on this host
5257 _DtActDeleteChildRec(invp,childp);
5258 SET_INV_PENDING(invp->state);
5259 if ( ! invp->numChildren )
5260 RESET_INV_WORKING(invp->state);
5263 /* Retry, using the next host */
5264 PrepareAndExecuteAction(data->associatedWidget, request);
5269 if ( invp && childp )
5273 * How can we tell if the Invocation COMPLETE bit
5274 * needs to be set here?
5275 * How about if no invocation is pending or working?
5277 SET_INV_ERROR(invp->state);
5278 childp->childState = _DtActCHILD_FAILED;
5282 /* No more hosts (they all failed); put up error dialog */
5283 if (action->u.cmd.execHostCount <= 1)
5285 /* Be backwards compatible */
5286 CommandInvokerError(data->associatedWidget,
5288 msg + data->offset);
5292 MultiHostAccessError(data->associatedWidget, request->clonedAction->label,
5293 request->badHostList);
5298 _DtActExecutionLeafNodeCleanup(invp->id,NULL,0,True);
5299 _DtFreeRequest(request);
5300 XtFree(data->actionLabel);
5302 XtFree((char *)data);
5308 * This function maps a filename relative to 'host' to be relative to
5309 * 'newHost'. If newHost is NULL, then the local host is assumed.
5311 * The returned string must be freed by the caller.
5316 const char * curHost,
5319 const char * newHost )
5321 char buf[MAXPATHLEN];
5324 char *netpath = NULL;
5328 * Create the full path name relative to curHost
5331 buf[0]='\0'; /* empty string to start with */
5337 /* check if there is already a '/' separator */
5340 DtLastChar(buf,&chp,&clen);
5341 if ( !( (clen == 1) && (*chp == '/')) )
5347 /* We should have constructed a file name string now */
5348 myassert(buf[0] != '\0');
5352 if ( _DtIsSameHost(curHost,newHost) )
5355 * The current host is the same as the new host
5356 * so no file name translation is necessary
5358 return XtNewString(buf);
5361 * The current host is not the same as the new host -- find the
5362 * cannonical netfile name then reinterpret it on the new host.
5364 switch ( tt_ptr_error(netpath = tt_host_file_netfile(curHost,buf)) )
5371 case TT_ERR_DBAVAIL:
5374 case TT_ERR_DBEXIST:
5377 case TT_ERR_INTERNAL:
5386 switch ( tt_ptr_error(path = tt_host_netfile_file(newHost,netpath)) )
5393 case TT_ERR_DBAVAIL:
5396 case TT_ERR_DBEXIST:
5399 case TT_ERR_INTERNAL:
5410 * Convert the file path which is relative to curHost to be
5411 * relative to the local host.
5413 if ( _DtIsSameHost(curHost,NULL) )
5416 * The current host is the same as the local host
5417 * so no file name translation is necessary
5419 return XtNewString(buf);
5422 * The current host is not the same as the local host -- find the
5423 * cannonical netfile name then reinterpret it on the local host.
5425 switch ( tt_ptr_error(netpath = tt_host_file_netfile(curHost,buf)) )
5432 case TT_ERR_DBAVAIL:
5435 case TT_ERR_DBEXIST:
5438 case TT_ERR_INTERNAL:
5447 switch ( tt_ptr_error(path = tt_netfile_file(netpath)) )
5454 case TT_ERR_DBAVAIL:
5457 case TT_ERR_DBEXIST:
5460 case TT_ERR_INTERNAL:
5471 * Free up the memory allocated by tooltalk filenaming code here so
5472 * downstream code need not worry about it.
5479 path = XtNewString(s);