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: ActionTt.c /main/12 1999/09/16 14:56:00 mgreess $ */
25 * (c) Copyright 1997, The Open Group
27 /*************************************<+>*************************************
28 *****************************************************************************
32 ** Project: CDE Execution Management
34 ** Description: This file contains the Tooltalk portions of the
35 ** action library source code.
38 ** by Hewlett-Packard Company
41 ** (c) Copyright 1993, 1994 Hewlett-Packard Company
42 ** (c) Copyright 1993, 1994 International Business Machines Corp.
43 ** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
44 ** (c) Copyright 1993, 1994 Novell, Inc.
45 ****************************************************************************
46 ************************************<+>*************************************/
48 /******************************************************************************
50 * TT_Message Key Assignments For Attached Data (please note additions here!)
52 * 0 - client data 5/94 --tg
53 * 1 - ms_timeout 7/94 --tg
54 * 2 - invocation id - currently unused
55 * 3 - child id - currently unused
57 *****************************************************************************/
62 #include <sys/types.h>
64 #include <sys/param.h>
66 #ifdef _SUN_OS /* Need this for the strtod () call */
67 #include <floatingpoint.h>
74 #include <X11/Intrinsic.h>
79 #include <Dt/Message.h>
80 #include <Dt/Connect.h>
81 #include <Dt/Indicator.h>
82 #include <Dt/DtNlUtils.h>
83 #include <Dt/CommandM.h>
84 #include <Dt/Utility.h>
86 #include <Dt/Service.h>
88 #include <Dt/ActionUtilP.h>
89 #include <Dt/ActionDb.h>
90 #include <Dt/ActionFind.h>
95 #include <Xm/BulletinB.h>
96 #include <Xm/DialogS.h>
99 #include <Xm/LabelG.h>
100 #include <Xm/TextF.h>
101 #include <Xm/SeparatoG.h>
102 #include <Xm/PushBG.h>
103 #include <Xm/MessageB.h>
104 #include <Xm/MwmUtil.h>
106 #include <Dt/ActionP.h>
107 #include <Dt/Action.h>
108 #include <Dt/EnvControlP.h>
110 #include "myassertP.h"
111 #include "DtSvcLock.h"
113 /******** Public Function Declarations ********/
114 void _DtProcessTtRequest(
116 ActionRequest *request,
118 char * relPathDir ) ;
120 Tt_status _DtInitializeToolTalk(Widget w);
122 extern void _DtCreateErrorDialog(
126 extern Boolean _DtCompileMessagePiece(
128 ActionRequest *request,
133 unsigned long processingMask,
134 Boolean ** paramUsed,
135 int * promptDataIndex ) ;
136 extern ActionRequest * _DtCloneRequest (
137 ActionRequest * request) ;
138 extern void _DtFreeRequest(
139 ActionRequest *request) ;
140 /******** End Public Function Declarations ********/
144 /******** Static Function Declarations ********/
146 static Boolean ResolveTtRequestMessagePieces(
148 ActionRequest *request,
150 char * relPathDir ) ;
152 static void InitiateTtRequest(
154 ActionRequest *request ) ;
156 static Tt_callback_action TtRequestCallbackHandler(
158 Tt_pattern pattern ) ;
160 static char * WrapMessageLines(
163 static void ReportToolTalkError(
168 static Tt_callback_action _DtActReceiveExecId(
171 static Tt_callback_action _DtActReceiveDoneMsg(
174 static Tt_message _DtTtContractIgnoreMsgCB(
177 Tt_message contract);
179 /******** End Static Function Declarations ********/
183 /* Pointers to localizable strings */
184 /******************************************************************************
186 These Strings are static to the Action.c File. Defining these here
187 creates a new strings static to this file which are never initialized.
188 References to these uninitialized strings by the GETMESSAGE11 macro
189 will cause a core dump.
191 For the short term, to keep things running -- I've marked those
192 strings actually used in this file as external both here and in
193 Action.c. This violates naming conventions and the desire to keep
194 global symbols to a minimum. We should come up with a permanent
195 solution for this problem later.
197 static String PromptDialogTitle;
198 static String ErrorPostfix;
199 static String PromptDialogLabel;
200 static String ContinueMessage;
201 static String HostErrorMsg;
202 static String HostErrorMsg2;
203 static String NoActionMsg;
204 static String MapErrorMsg;
205 static String ArgumentErrorMsg;
206 static String NoActionMsg2;
207 static String InvalidFileMsg;
208 static String MultiHostErrorMsg;
209 static String IcccmReqErrorMsg;
210 static String NoToolTalkConnMsg;
211 **************************************************************************/
212 extern String ToolTalkErrorMsg;
213 extern String ToolTalkErrorMsg2;
214 extern String TtFileArgMapErr;
217 /*****************************************************************************
219 * Routines to field message patterns from dtexec
221 *****************************************************************************/
223 static Tt_callback_action _DtActReceiveExecId(
230 _DtActChildRecT *childp;
232 switch(tt_message_arg_ival(msg,0,&invId))
234 case TT_OK: /* got invocation Id (arg 0 value ) */
236 case TT_ERR_NOMP: /* tt_session_not_running */
237 case TT_ERR_NUM: /* integer value out of range */
238 case TT_ERR_POINTER: /* pointer does not point to correct object */
239 return TT_CALLBACK_CONTINUE;
242 return TT_CALLBACK_CONTINUE;
245 switch(tt_message_arg_ival(msg,1,&childId) ) /* child Id (arg 1 value ) */
247 case TT_OK: /* got child Id (arg 1 value ) */
249 case TT_ERR_NOMP: /* tt_session_not_running */
250 case TT_ERR_NUM: /* integer value out of range */
251 case TT_ERR_POINTER: /* pointer does not point to correct object */
252 return TT_CALLBACK_CONTINUE;
255 return TT_CALLBACK_CONTINUE;
258 switch ( tt_ptr_error(procId = tt_message_arg_val(msg,2)) )
260 case TT_OK: /* got dtexec's proc Id (arg 2 value ) */
262 case TT_ERR_NOMP: /* tt_session_not_running */
263 case TT_ERR_NUM: /* integer value out of range */
264 case TT_ERR_POINTER: /* pointer does not point to correct object */
265 return TT_CALLBACK_CONTINUE;
268 return TT_CALLBACK_CONTINUE;
273 * Save the proc Id for dtexec returned in this message
275 childp = _DtActFindChildRec( invId, childId );
276 myassert(childp); /* we should be able to find the child record */
281 * This should be a command action
283 myassert( IS_CMD(childp->mask) );
284 childp->u.cmd.TTProcId = XtNewString( procId );
287 * Note that the child HAS identified itself.
289 if (childp->childState == _DtActCHILD_ALIVE_UNKNOWN)
290 childp->childState = _DtActCHILD_ALIVE;
293 tt_message_reply(msg);
294 tttk_message_destroy(msg);
296 return TT_CALLBACK_PROCESSED;
299 static Tt_callback_action _DtActReceiveDoneMsg(
305 _DtActChildRecT *childp;
306 DtActionArg *retArgv; /* returnable arguments */
307 int retArgc; /* returnable argument count */
308 int doneCode; /* returnable _DtActCHILD_ code */
310 switch(tt_message_arg_ival(msg,0,&invId))
312 case TT_OK: /* got invocation Id (arg 0 value ) */
314 case TT_ERR_NOMP: /* tt_session_not_running */
315 case TT_ERR_NUM: /* integer value out of range */
316 case TT_ERR_POINTER: /* pointer does not point to correct object */
317 return TT_CALLBACK_CONTINUE;
320 return TT_CALLBACK_CONTINUE;
324 switch(tt_message_arg_ival(msg,1,&childId) ) /* child Id (arg 1 value ) */
326 case TT_OK: /* got child Id (arg 1 value ) */
328 case TT_ERR_NOMP: /* tt_session_not_running */
329 case TT_ERR_NUM: /* integer value out of range */
330 case TT_ERR_POINTER: /* pointer does not point to correct object */
331 return TT_CALLBACK_CONTINUE;
334 return TT_CALLBACK_CONTINUE;
338 switch(tt_message_arg_ival(msg,2,&doneCode) ) /* done code (arg 2 value ) */
340 case TT_OK: /* got done code (arg 2 value ) */
342 case TT_ERR_NOMP: /* tt_session_not_running */
343 case TT_ERR_NUM: /* integer value out of range */
344 case TT_ERR_POINTER: /* pointer does not point to correct object */
345 return TT_CALLBACK_CONTINUE;
348 return TT_CALLBACK_CONTINUE;
353 * In some cases, dtexec may send a _DtActDtexecDone(Request) but
354 * be under pressure to go down ASAP and not wait around for a Reply.
356 tt_message_reply(msg);
357 tttk_message_destroy(msg);
359 childp = _DtActFindChildRec( invId, childId );
360 myassert(childp); /* we should be able to find the child record */
363 * Init return args incase there is not a childp.
370 childp->childState = doneCode; /* usually _DtActCHILD_DONE */
371 retArgc = _DtActGetCmdReturnArgs(invId,childp,&retArgv);
373 _DtActExecutionLeafNodeCleanup(invId, retArgv, retArgc, True);
375 return TT_CALLBACK_PROCESSED;
379 /******************************************************************************
380 ******************************************************************************
382 * Routine to query DtActionQuit() behavior if done now.
386 * DtActionInvocationID id,
389 * This function has been obsoleted. See revision
390 * number 1.6 for the implementation.
392 ******************************************************************************
393 *****************************************************************************/
395 /******************************************************************************
396 ******************************************************************************
398 * Routines to quit actions.
400 * static Tt_callback_action
402 * Tt_message message,
403 * Tt_pattern pattern)
405 * This function has been obsoleted. See revision
406 * number 1.6 for the implementation.
408 ******************************************************************************
409 *****************************************************************************/
411 /******************************************************************************
413 * Routine used to Quit a specific action. Note
414 * that status is returned via the childState
418 * _DtActionQuitChild(
419 * DtActionInvocationID id,
420 * _DtActChildRecT *childRecP,
423 * XtAppContext context,
426 * This function has been obsoleted. See revision
427 * number 1.6 for the implementation.
429 ******************************************************************************/
431 /******************************************************************************
435 * Routine used to Quit all actions associated with
436 * a DtActionInvocationID. CMD and TT actions will
437 * be quit-able using the same code.
441 * DtActionInvocationID id,
442 * unsigned long ms_timeout,
445 * This function has been obsoleted. See revision
446 * number 1.6 for the implementation.
448 ******************************************************************************/
450 /******************************************************************************
451 ******************************************************************************
455 ******************************************************************************
456 *****************************************************************************/
469 buf = XtMalloc(strlen(ToolTalkErrorMsg2) + strlen(errorMsg) + 10);
470 sprintf(buf, ToolTalkErrorMsg2, errorMsg);
473 buf = XtNewString(ToolTalkErrorMsg);
474 msg = XmStringCreateLocalized(buf);
475 _DtCreateErrorDialog(w, actionName, msg);
482 /******************************************************************************
483 ******************************************************************************
485 * ToolTalk Specific Functions
487 ******************************************************************************
488 *****************************************************************************/
490 /******************************************************************************
492 * This is the entry point into the ToolTalk message world. All of the code
493 * before this has been written to handle any of the different transport
494 * types. The code from this point on will know specifically how to
495 * process a ToolTalk message. We will start by taking each
496 * of the pieces of information making up the ToolTalk message, and
497 * resolving any of the keywords, by replacing them with the appropriate
498 * information. If this fails (which it only should do if we try to map
499 * a file to a host which cannot be accessed), then we will terminate the
500 * request, posting an error dialog for the user. Unlike a Command Invoker
501 * request, where we may try several times to handle it (once for each
502 * of the specified execution hosts), a ToolTalk message is handled only
508 ActionRequest *request,
513 ActionPtr action = request->clonedAction;
515 if (ResolveTtRequestMessagePieces(w, request, relPathHost, relPathDir))
518 * Issue the request; the success/failure notification comes
519 * asynchronously; that's when everything gets cleaned up.
521 InitiateTtRequest( w, request );
525 /* Display error dialog */
527 * fdt: there really needs to be some policy defining where an error
528 * message will be returned. Also, is this really the right message
529 * to display here? Was the failure really caused by a file mapping
530 * problem? In fact, is this a case we even can get to???? [Yes]
532 /* fdt: this string must be localized */
533 ReportToolTalkError(w, request->clonedAction->label, TtFileArgMapErr);
538 /******************************************************************************
540 * This function takes all of the pieces making up a ToolTalk message,
541 * and resolves any references to keywords, using both the passed-in
542 * arguments, and any information collected from the prompt dialog.
545 ResolveTtRequestMessagePieces(
547 ActionRequest *request,
552 ActionPtr action = request->clonedAction;
553 tt_msgAttr * tt = &(action->u.tt_msg);
556 Boolean * paramUsed = NULL;
557 int promptDataIndex = 0;
559 if (_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
560 &(tt->tt_op), True, 0, ¶mUsed, &promptDataIndex)
562 _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
563 &(tt->tt_file), False, 0, ¶mUsed, &promptDataIndex))
565 for (i = 0, success = True; (i < tt->value_count) && success; i++)
567 success = _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
568 &(tt->tt_argn_value[i]), False, _DTAct_TT_ARG, ¶mUsed, &promptDataIndex);
570 for (i = 0; (i < tt->vtype_count) && success; i++)
572 success = _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
573 &(tt->tt_argn_vtype[i]), False, _DTAct_TT_VTYPE, ¶mUsed, &promptDataIndex);
578 /* We must have at least the op strings */
579 if ((tt->tt_op.compiledMessage) &&
580 (strlen(tt->tt_op.compiledMessage) > 0))
588 /* ERROR: Free up any intermediate work we've done here */
589 XtFree(tt->tt_op.compiledMessage);
590 XtFree(tt->tt_file.compiledMessage);
591 tt->tt_op.compiledMessage = NULL;
592 tt->tt_file.compiledMessage = NULL;
593 for (i = 0; i < tt->value_count; i++)
595 XtFree(tt->tt_argn_value[i].compiledMessage);
596 tt->tt_argn_value[i].compiledMessage = NULL;
598 for (i = 0; i < tt->vtype_count; i++)
600 XtFree(tt->tt_argn_vtype[i].compiledMessage);
601 tt->tt_argn_vtype[i].compiledMessage = NULL;
607 /******************************************************************************
608 ******************************************************************************
610 * Routines to handle Tooltalk responses.
612 ******************************************************************************
613 *****************************************************************************/
615 /******************************************************************************
617 * Translate a ttmedia_load() callback into a regular
618 * looking DtActionCallbackProc() for the user.
621 Ttmedia_to_Dt_StatusUpdateCB(
625 unsigned char *contents,
633 Boolean wrapMessageLines = False;
634 _DtActChildRecT *childRec;
635 _DtActInvRecT *invRec;
636 DtActionInvocationID id;
637 DtActionArg *newArgp = NULL; /* hanger for returned data if any */
639 unsigned long evalStatus;
640 DtActionStatus userStatus;
645 status = (Tt_status) tt_message_status(message);
646 state = (Tt_state) tt_message_state(message);
648 if (state == TT_STARTED) {
650 * Handler is just getting started. Eat it.
652 tttk_message_destroy( message );
653 return( (Tt_message) NULL ); /* like TT_CALLBACK_PROCESSED */
656 if ( (state != TT_HANDLED) && (state != TT_FAILED) &&
657 (state != TT_RETURNED) && (state != TT_SENT) )
660 * This address space is probably the handler. Pass
661 * on it, so that the handler gets a chance to handle it.
663 return( (Tt_message) message ); /* like TT_CALLBACK_CONTINUE */
667 * Process a TT_FAILED, TT_HANDLED, TT_RETURNED or TT_SEND related to our
672 * Use ttmedia's client data capability since it can associate our
673 * original client data with any reply *or* new request associated
674 * with the original request.
676 * data = (CallbackData *) tt_message_user( message, 0 ); not good enough
678 data = (CallbackData *) clientdata;
681 invRec = _DtActFindInvRec( id );
683 childRec = _DtActFindChildRec( id, data->childId );
685 if (state == TT_FAILED) {
686 if (status != TT_DESKTOP_ECANCELED) {
688 * Determine whether ToolTalk or the receiver failed the message
690 if (status < TT_ERR_LAST) {
691 errorMsg = tt_status_message(status);
692 wrapMessageLines = True;
695 errorMsg = tt_message_status_string(message);
698 if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) {
699 if (wrapMessageLines)
700 errorMsg = WrapMessageLines(errorMsg);
701 ReportToolTalkError(data->associatedWidget,
702 data->actionLabel, errorMsg);
703 if (wrapMessageLines)
707 ReportToolTalkError(data->associatedWidget,
708 data->actionLabel, NULL);
712 else /* if (state == TT_HANDLED) or other things (?) */ {
714 * We have a possible update - see if user wants arg back
717 newArgc = invRec->ac;
718 newArgp = _DtActMallocEmptyArgArray( newArgc );
721 * Our only return argument is TT_ARG0_VALUE or TT_FILE.
723 * If a docname went out, it went out as TT_IN, so we
724 * won't need to push it back up.
726 * We will lean on the fact that we carefully selected
727 * what action definitions were run through the ttmedia
728 * machinery, and that we can assume from the ttmedia
729 * reply where the arguments go back.
734 * DtACTION_BUFFER - possible update.
738 * Calculate index into original argument list from the
741 upIdx = childRec->argMap[i].argIdx;
744 if ( (IS_BUFFER_OBJ(invRec->info[upIdx].mask) &&
745 IS_WRITE_OBJ(invRec->info[upIdx].mask)) ) {
747 * Need to push this object up.
753 upVType = tt_message_arg_type( message, i );
755 newArgp[upIdx].argClass = DtACTION_BUFFER;
756 newArgp[upIdx].u.buffer.size = len;
757 newArgp[upIdx].u.buffer.type = XtNewString(upVType);
758 newArgp[upIdx].u.buffer.writable = 1;
760 newArgp[upIdx].u.buffer.bp = (void *) XtMalloc(len);
761 memcpy( newArgp[upIdx].u.buffer.bp, contents, len );
768 * Done with the data.
770 tt_free((char *) contents);
778 * Calculate index into original argument list from the
779 * user. Notice that we're shipping data because of TT_FILE
780 * and not TT_ARG0_FILE. To do this, argMap is arranged
782 * argMap[ ARG0, ... , ARGn, TT_FILE].argIdx
784 * The array-index for TT_FILE is *one beyond* all the normal
785 * TT_ARGn_VALUE array-index values. The .argN for TT_FILE
788 * As the spec says, the action service "may choose to"
789 * return objects, which is why it is o.k. to return
790 * their "TT_FILE %Arg_n%" value for less than obvious
793 upIdx = childRec->argMap[i+1].argIdx;
796 newArgp[upIdx].argClass = DtACTION_FILE;
797 newArgp[upIdx].u.file.name = XtNewString( file );
805 * If a final response to our original message, we're done.
807 if ((message == childRec->u.tt.reqMessage) && (state == TT_HANDLED))
808 childRec->childState = _DtActCHILD_DONE;
809 else if ((message == childRec->u.tt.reqMessage) && (state == TT_FAILED)) {
810 if (status == TT_DESKTOP_ECANCELED)
811 childRec->childState = _DtActCHILD_CANCELED;
813 childRec->childState = _DtActCHILD_FAILED;
817 * If this was a deposit, reply that we've handled it.
819 if (op == TTME_DEPOSIT) {
820 tt_message_reply( message );
822 * Cleanup here since this message is not part of our long
825 tttk_message_destroy( message );
829 * Cleanup message - handled by _DtActExecutionLeafNodeCleanup()
831 * tttk_message_destroy( message );
834 _DtActExecutionLeafNodeCleanup( id, newArgp, newArgc, 1 );
836 return( (Tt_message) NULL ); /* message consumed */
840 /******************************************************************************
842 * Guess at the REP_TYPE for a given argument.
843 * String is just a variation of a buffer, so
844 * DtACT_TT_REP_BUFFER is returned for buffers and
847 static int _DtAct_tt_message_arg_reptype( Tt_message message, int arg )
853 status = tt_message_arg_ival( message, arg, &testVal );
856 return( DtACT_TT_REP_INT);
857 else if (status == TT_ERR_NUM)
858 return( DtACT_TT_REP_BUFFER );
861 /******************************************************************************
863 * Translate a free-form request callback into a
864 * regular looking DtActionCallbackProc() for the user.
866 static Tt_callback_action
867 TtRequestCallbackHandler(
876 Boolean wrapMessageLines = False;
877 _DtActChildRecT *childRec;
878 _DtActInvRecT *invRec;
879 DtActionInvocationID id;
880 unsigned long evalStatus;
881 DtActionStatus userStatus;
882 DtActionArg *newArgp = NULL; /* hanger for returned data if any */
883 int newArgc = 0, totalArgs, i, j, upIdx;
887 int upttbuflen, ivalue;
888 char *upVType, *upVType2;
891 status = (Tt_status) tt_message_status(message);
892 state = (Tt_state) tt_message_state(message);
894 if (state == TT_STARTED) {
896 * Handler is just getting started. Eat it.
898 return TT_CALLBACK_PROCESSED;
901 if ((state != TT_HANDLED) && (state != TT_FAILED) && (state != TT_RETURNED))
904 * This address space is probably the handler. Pass
905 * on it, so that the handler gets a chance to handle it.
907 return TT_CALLBACK_CONTINUE;
911 * Process a TT_FAILED, TT_HANDLED or TT_RETURNED related to our
914 if ( !(data = (CallbackData *) tt_message_user( message, 0 )) ) {
915 myassert(data); /* data should always be non-null */
916 return( (Tt_callback_action) TT_CALLBACK_PROCESSED );
920 invRec = _DtActFindInvRec( id );
922 childRec = _DtActFindChildRec( id, data->childId );
925 if (state == TT_FAILED) {
926 if (status != TT_DESKTOP_ECANCELED) {
928 * Determine whether ToolTalk or the receiver failed the message
930 if (status < TT_ERR_LAST) {
931 errorMsg = tt_status_message(status);
932 wrapMessageLines = True;
935 errorMsg = tt_message_status_string(message);
938 if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) {
939 if (wrapMessageLines)
940 errorMsg = WrapMessageLines(errorMsg);
941 ReportToolTalkError(data->associatedWidget,
942 data->actionLabel, errorMsg);
946 ReportToolTalkError(data->associatedWidget,
947 data->actionLabel, NULL);
951 else if (state == TT_HANDLED) {
955 newArgc = invRec->ac;
956 newArgp = _DtActMallocEmptyArgArray( newArgc );
958 totalArgs = tt_message_args_count(message);
961 * Look through all the arguments returned in the request.
963 for ( i = 0; i < totalArgs; i++ ) {
965 * Look through the child's arg list to see which ones need to
968 upIdx = childRec->argMap[i].argIdx;
971 if ( (IS_BUFFER_OBJ(invRec->info[upIdx].mask) &&
972 IS_WRITE_OBJ(invRec->info[upIdx].mask)) ||
973 IS_FILE_OBJ(invRec->info[upIdx].mask) ) {
975 * Need to push this object up.
981 if ( IS_BUFFER_OBJ(invRec->info[upIdx].mask) )
982 upArgClass = DtACTION_BUFFER;
983 else if ( IS_FILE_OBJ(invRec->info[upIdx].mask) )
984 upArgClass = DtACTION_FILE;
985 else if ( IS_STRING_OBJ( invRec->info[upIdx].mask) )
986 upArgClass = DtACTION_STRING;
988 upArgClass = DtACTION_NULLARG; /* error situation */
993 upVType = tt_message_arg_type( message, i );
998 argRepType = _DtAct_tt_message_arg_reptype(message, i);
1001 * If we think TT_REP_BUFFER so far, but don't have
1002 * an argClass of DtACTION_BUFFER, then we really have
1003 * a DtACT_TT_REP_STRING.
1005 if ( (argRepType == DtACT_TT_REP_BUFFER) &&
1006 !(upArgClass == DtACTION_BUFFER) )
1007 argRepType = DtACT_TT_REP_STRING;
1010 * Do appropriate unpacking.
1012 switch (argRepType) {
1013 case DtACT_TT_REP_BUFFER:
1014 case DtACT_TT_REP_STRING:
1016 * By convention, if we're here, the buffer
1017 * is writable. Also, STRINGs can be
1018 * fetched using bval.
1020 status = tt_message_arg_bval( message, i,
1021 (unsigned char **) &upttbuf,
1024 if ( status != TT_OK ) {
1026 * Give up - something bad happened.
1028 upArgClass = DtACTION_NULLARG;
1031 case DtACT_TT_REP_INT:
1033 * Unpack an integer.
1035 status = tt_message_arg_ival( message, i,
1037 if ( status != TT_OK ) {
1038 upArgClass = DtACTION_NULLARG;
1042 * Fake up enough information that we
1043 * could return the argument in either
1044 * a DtACTION_FILE or DtACTION_BUFFER
1045 * argClass argument.
1048 upttbuf = XtMalloc( upttbuflen );
1049 sprintf( upttbuf, "%u", ivalue );
1052 case DtACT_TT_REP_UNDEFINED:
1054 * Cannot figure out how to unpack arg.
1056 upArgClass = DtACTION_NULLARG;
1060 switch (upArgClass) {
1061 case DtACTION_NULLARG:
1063 * We failed above, so give up.
1066 case DtACTION_BUFFER:
1068 * Convert from tt_free()-able to
1071 newArgp[upIdx].argClass = upArgClass;
1072 newArgp[upIdx].u.buffer.size = upttbuflen;
1073 newArgp[upIdx].u.buffer.type = XtNewString(
1075 newArgp[upIdx].u.buffer.writable = 1;
1077 newArgp[upIdx].u.buffer.bp = (void *)
1078 XtMalloc( upttbuflen );
1079 memcpy( newArgp[upIdx].u.buffer.bp, upttbuf,
1082 if (argRepType == DtACT_TT_REP_INT) {
1084 * upttbuf was XtMalloc'ed above
1092 * Convert from tt_free()-able to
1095 newArgp[upIdx].argClass = upArgClass;
1096 newArgp[upIdx].u.file.name = XtNewString(
1099 if (argRepType == DtACT_TT_REP_INT) {
1101 * upttbuf was XtMalloc'ed above
1118 * If a final response to our original message, we're done.
1120 if ((message == childRec->u.tt.reqMessage) && (state == TT_HANDLED))
1121 childRec->childState = _DtActCHILD_DONE;
1122 else if ((message == childRec->u.tt.reqMessage) && (state == TT_FAILED)) {
1123 if (status == TT_DESKTOP_ECANCELED)
1124 childRec->childState = _DtActCHILD_CANCELED;
1126 childRec->childState = _DtActCHILD_FAILED;
1130 * Message is destroyed by _DtActExecutionLeafNodeCleanup()
1133 _DtActExecutionLeafNodeCleanup( id, newArgp, newArgc, 1 );
1135 return( (Tt_callback_action) TT_CALLBACK_PROCESSED ); /* message consumed */
1138 /******************************************************************************
1139 ******************************************************************************
1141 * Process a ToolTalk request.
1143 ******************************************************************************
1144 *****************************************************************************/
1146 /******************************************************************************
1148 * _DtTtContractIgnoreMsgCB
1150 * Contract callback to ignore all messages received. This is consume
1151 * status messages sent back by some message handlers. If these messages
1152 * aren't consumed by the library, then a tooltalk client can become
1153 * confused and also spurious error messages are written to syslog or the
1155 ******************************************************************************/
1158 _DtTtContractIgnoreMsgCB(
1161 Tt_message contract)
1164 * Artificially consume unwanted messages.
1166 tttk_message_abandon(msg);
1174 ActionRequest *request )
1178 ActionPtr action = request->clonedAction;
1179 tt_msgAttr * tt = &(action->u.tt_msg);
1183 int destroy_message = 0;
1191 unsigned char * contents;
1195 Tt_pattern *subcon_patterns;
1196 _DtActInvRecT *actInvRecP;
1197 _DtActChildRecT *actChildRecP;
1198 unsigned long evalStatus;
1199 DtActionStatus userStatus;
1201 char *bufFilename = NULL;
1206 * Find associated long term records for this request
1208 actInvRecP = _DtActFindInvRec( request->invocId );
1209 myassert(actInvRecP);
1210 actChildRecP = _DtActFindChildRec( request->invocId, request->childId );
1211 myassert(actChildRecP);
1213 actChildRecP->childState = _DtActCHILD_UNKNOWN; /* really don't know */
1218 * Consider doing a check of argClass -vs- TT_ARGn_REP_TYPE,
1219 * and issue errors where appropriate.
1221 * argClass=DtACTION_FILE
1222 * o REP_UNDEFINED - o.k. - will guess convert to REP_STRING
1223 * o REP_BUFFER - error - no sense, use DtACTION_BUFFER
1224 * o REP_STRING - o.k. - normal case
1225 * o REP_INTEGER - o.k. - normal case
1227 * argClass=DtACTION_BUFFER
1228 * o REP_UNDEFINED - o.k. - will guess convert to REP_BUFFER
1229 * o REP_BUFFER - o.k. - normal case
1230 * o REP_STRING - ? - will try to handle
1231 * o REP_INTEGER - error - use DtACTION_FILE
1235 * Test request for ttmedia_load() suitability.
1237 * To be ttmedia_load() suitable, action defs must look like:
1239 * Action TtmediaBufferStyle
1246 * TT_CLASS TT_REQUEST
1247 * TT_SCOPE TT_SESSION
1248 * TT_FILE --- must be null or unset ---
1250 * TT_ARG0_MODE <mode>
1251 * TT_ARG0_VTYPE <vtype> | %Arg_n%
1252 * TT_ARG0_REP_TYPE TT_REP_BUFFER or TT_REP_UNDEFINED
1253 * TT_ARG0_VALUE %Arg_n%
1255 * optional TT_ARG1 for docname
1257 * TT_ARG1_MODE TT_IN
1258 * TT_ARG1_VTYPE title
1259 * TT_ARG1_REP_TYPE TT_REP_STRING
1260 * TT_ARG1_VALUE <docname> | %Arg_n%
1264 * Action TtmediaFileStyle
1271 * TT_CLASS TT_REQUEST
1272 * TT_SCOPE TT_SESSION
1273 * TT_FILE <filename> | %(File)Arg_n%
1275 * TT_ARG0_MODE <mode>
1276 * TT_ARG0_VTYPE <vtype> | %Arg_n%
1277 * TT_ARG0_REP_TYPE TT_REP_STRING
1278 * TT_ARG0_VALUE --- must be null or unset ---
1280 * optional TT_ARG1 for docname
1282 * TT_ARG1_MODE TT_IN
1283 * TT_ARG1_VTYPE title
1284 * TT_ARG1_REP_TYPE TT_REP_STRING
1285 * TT_ARG1_VALUE <docname> | %Arg_n%
1289 ttmedia_test = 0; /* default to free-form */
1291 if ( (tt->tt_class == TT_REQUEST) &&
1292 (tt->tt_scope == TT_SESSION) &&
1293 ((tt->mode_count == 1) || (tt->mode_count == 2)) &&
1294 (tt->tt_argn_rep_type[0] != DtACT_TT_REP_INT) ) {
1297 * 1- and 2-arg session-scoped requests not using ivals
1298 * can be sent via ttmedia_load()...
1303 * ...as long as the second argument is the title. So:
1304 * Allow for an optional "docname" in TT_ARG1. If there is a
1305 * second argument, it must follow some specific rules or we'll
1306 * disqualify this request.
1308 if (tt->mode_count == 2) {
1309 if ((tt->tt_argn_mode[1] != TT_IN) ||
1310 (tt->tt_argn_rep_type[1] != DtACT_TT_REP_STRING) ||
1311 (strcmp( tt->tt_argn_vtype[1].compiledMessage, "title" )) ) {
1319 /**********************************************************************
1323 * Special case media-load requests *and* any other requests that
1324 * follow the "ttmedia style".
1326 * If rep_type is INT, then ttmedia_load() cannot be used, and
1327 * a rep_type of UNDEFINED is too chancy to guess on.
1329 * Using ttmedia_load() sets up additional machinery.
1333 Tttk_op op = tttk_string_op(tt->tt_op.compiledMessage);
1336 * Some media ops are overloaded, and the actual media op that
1337 * should be used depends on the TT_ARGn_MODE of the argument.
1339 switch (tt->tt_argn_mode[0]) {
1351 contents = (unsigned char *) NULL;
1354 * Determine what the data looks like: file or a buffer?
1356 if ( tt->tt_argn_rep_type[0] == DtACT_TT_REP_BUFFER
1357 || tt->tt_argn_rep_type[0] == DtACT_TT_REP_UNDEFINED)
1360 * Passing a buffer of data.
1364 * See if we should pluck the buffer out of a tmp file.
1366 length = -1; /* -1 means not a buffer */
1367 if ( actChildRecP->argMap[0].argIdx != -1 ) {
1368 if ( IS_BUFFER_OBJ(actInvRecP->
1369 info[actChildRecP->argMap[0].argIdx].mask) ) {
1370 length = actInvRecP->
1371 info[actChildRecP->argMap[0].argIdx].size;
1372 bufFilename = actInvRecP->
1373 info[actChildRecP->argMap[0].argIdx].name;
1379 contents = (unsigned char *)
1380 _DtActReadTmpFileToBuffer(bufFilename, &length);
1381 /* length plucked from above */
1384 contents = (unsigned char *)
1385 tt->tt_argn_value[0].compiledMessage;
1386 length = tt->tt_argn_value[0].msgLen;
1389 file = (char *) tt->tt_file.compiledMessage;
1390 if (tt->mode_count == 2)
1391 docname = (char *) tt->tt_argn_value[1].compiledMessage;
1393 docname = (char *) NULL;
1396 * Fill out a callback structure.
1398 data = (CallbackData *)XtMalloc(sizeof(CallbackData));
1399 data->actionLabel = XtNewString(request->clonedAction->label);
1400 data->associatedWidget = w;
1401 data->actionPtr = action;
1402 data->requestPtr = _DtCloneRequest(request);
1403 data->actInvId = actInvRecP->id;
1404 data->childId = actChildRecP->childId;
1407 * Let tttk build the message and setup additional machinery.
1409 message = ttmedia_load( (Tt_message) 0, /* Am top level req */
1410 Ttmedia_to_Dt_StatusUpdateCB,
1413 tt->tt_argn_vtype[0].compiledMessage,
1419 status = tt_ptr_error(message);
1420 if (! tt_is_err(status))
1423 * (Re)set the op. This allows arbitrary Media-like
1424 * TT_MSG actions to use the Media action machinery.
1426 tt_message_op_set(message, tt->tt_op.compiledMessage);
1429 * Attach client data to message using slot 0.
1431 tt_message_user_set(message, 0, (void *) data);
1433 actChildRecP->childState = _DtActCHILD_PENDING_START; /* starting */
1436 * I'm assuming w is non-NULL, hence the lack of a callback
1437 * and callback data.
1439 subcon_patterns = ttdt_subcontract_manage(message,
1440 (Ttdt_contract_cb) _DtTtContractIgnoreMsgCB,
1443 status = tt_message_send(message);
1444 destroy_message = 1;
1450 if (status != TT_OK) {
1452 * The child error'ed out.
1454 actChildRecP->childState = _DtActCHILD_FAILED;
1457 * Report the fact that the request failed
1459 errorMsg = tt_status_message(status);
1460 if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg)
1462 errorMsg = WrapMessageLines(errorMsg);
1463 ReportToolTalkError(w, request->clonedAction->label, errorMsg);
1467 ReportToolTalkError(w, request->clonedAction->label, NULL);
1470 * Free information since no callbacks will be triggered:
1471 * - Callback Client Data
1474 XtFree(data->actionLabel);
1475 _DtFreeRequest(data->requestPtr);
1476 XtFree((char *)data);
1478 if (destroy_message)
1479 tttk_message_destroy(message);
1483 * The child is alive and well (so far as we can tell).
1485 actChildRecP->childState = _DtActCHILD_ALIVE;
1488 * Stash data away for long term usage.
1490 actChildRecP->u.tt.TTProcId = tt_message_sender( message );
1491 actChildRecP->u.tt.reqMessage = message;
1492 actChildRecP->u.tt.isTtMedia = 1;
1493 actChildRecP->u.tt.TtMediaOp = op;
1494 actChildRecP->u.tt.subConPats = subcon_patterns;
1498 * Throw away transition buffer if there was one.
1501 XtFree((char *) contents);
1505 /**********************************************************************
1509 * All other Requests and Notices go out "free form". Do best case.
1511 message = tttk_message_create( 0, tt->tt_class, tt->tt_scope, 0,
1512 tt->tt_op.compiledMessage,
1513 TtRequestCallbackHandler );
1515 tt_message_file_set(message, tt->tt_file.compiledMessage);
1518 * Process each of the message arguments
1520 * note: As of CDE 1.0, all the tt->xxx_count variables are
1521 * set the same. I doubt they would ever diverge, and
1522 * much of this code would break if they did.
1524 for (i = 0; i < tt->mode_count; i++)
1527 * Pluck some data out of the argMap to help us make
1528 * some decisions here.
1530 length = -1; /* -1 means not a buffer */
1531 bufFilename = (char *) NULL; /* possible buffer tmp file */
1533 if ( actChildRecP->argMap[i].argIdx != -1 ) {
1534 if ( IS_BUFFER_OBJ(actInvRecP->
1535 info[actChildRecP->argMap[i].argIdx].mask) ) {
1536 length = actInvRecP->
1537 info[actChildRecP->argMap[i].argIdx].size;
1538 bufFilename = actInvRecP->
1539 info[actChildRecP->argMap[i].argIdx].name;
1546 mode = tt->tt_argn_mode[i];
1549 * Determine REP TYPE
1551 if ((i < tt->rep_type_count) &&
1552 (tt->tt_argn_rep_type[i] != DtACT_TT_REP_UNDEFINED))
1554 /* The representation type was supplied; use it */
1555 repType = tt->tt_argn_rep_type[i];
1560 * No representation type given. If the argument was provided
1561 * and is a DtACTION_BUFFER, then use DtACT_TT_REP_BUFFER.
1562 * In all other cases, use DtACT_TT_REP_STRING.
1565 repType = DtACT_TT_REP_BUFFER;
1567 repType = DtACT_TT_REP_STRING;
1571 * Determine VALUE and VTYPE
1573 if (i < tt->value_count)
1576 * Use the specified vtype
1578 vtype = tt->tt_argn_vtype[i].compiledMessage;
1581 * Determine where to get the value from
1583 if ( length != -1 ) {
1585 * Have a buffer - see if it's value can be fetched
1586 * from a tmp file. If so, will need to free later!
1589 value = _DtActReadTmpFileToBuffer(bufFilename, &length);
1592 value = tt->tt_argn_value[i].compiledMessage;
1596 value = tt->tt_argn_value[i].compiledMessage;
1601 /* No value specified; use 'NULL' */
1605 * Use the specified vtype setting, if supplied; else
1608 if (i < tt->vtype_count)
1609 vtype = tt->tt_argn_vtype[i].compiledMessage;
1613 * Time to guess what the vtype should be. This
1616 if (repType == DtACT_TT_REP_INT)
1618 else if (repType == DtACT_TT_REP_STRING)
1620 else if (repType == DtACT_TT_REP_BUFFER)
1626 * Based on REP_TYPE, add the argument to the message. In
1627 * effect, rep_type is an instruction on how to package
1628 * the argument in the message.
1630 if (repType == DtACT_TT_REP_INT)
1633 intValue = strtol(value, &p, 0);
1637 tt_message_iarg_add(message, mode, vtype, intValue);
1639 else if (repType == DtACT_TT_REP_STRING)
1641 tt_message_arg_add(message, mode, vtype, value);
1643 else if (repType == DtACT_TT_REP_BUFFER)
1645 tt_message_barg_add(message, mode, vtype,
1646 (unsigned char *) value, length);
1649 if ( (length != -1) && (bufFilename) ) {
1651 * The value was from a buffer that we pulled from a tmp
1652 * file. Free the transition buffer since Tooltalk will have
1653 * made its own copy by now.
1659 if (tt->tt_class == TT_REQUEST)
1662 * For a request, we need to attach a callback, which will be
1663 * invoked when the reply is received. When the reply is
1664 * received, then we can free up the original message, and
1665 * determine whether we succeeded or failed.
1668 /* Fill out the callback structure */
1669 data = (CallbackData *)XtMalloc(sizeof(CallbackData));
1670 data->actionLabel = XtNewString(request->clonedAction->label);
1671 data->associatedWidget = w;
1672 data->actionPtr = action;
1673 data->requestPtr = _DtCloneRequest(request);
1674 data->actInvId = actInvRecP->id;
1675 data->childId = actChildRecP->childId;
1678 * Attach client data to message using slot 0.
1680 tt_message_user_set(message, 0, (void *) data);
1682 tt_message_callback_add(message, TtRequestCallbackHandler);
1685 actChildRecP->childState = _DtActCHILD_PENDING_START; /* starting */
1688 * I'm assuming w is non-NULL, hence the lack of a callback
1689 * and callback data.
1691 subcon_patterns = ttdt_subcontract_manage(message,
1692 (Ttdt_contract_cb) _DtTtContractIgnoreMsgCB,
1695 status = tt_message_send(message);
1697 /* See if we failed right off the bat */
1698 if (status != TT_OK)
1701 * The child error'ed out.
1703 * By marking the child as _DtActCHILD_DONE*, see the comment
1704 * at the end of this routine that describes how DtActionInvoke()
1705 * will do a child evaluation and cleanup.
1707 actChildRecP->childState = _DtActCHILD_FAILED;
1710 * Report the fact that the request failed
1712 errorMsg = tt_status_message(status);
1713 if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg)
1715 errorMsg = WrapMessageLines(errorMsg);
1716 ReportToolTalkError(w, request->clonedAction->label, errorMsg);
1720 ReportToolTalkError(w, request->clonedAction->label, NULL);
1723 * Clean up now, cause there will not be a reply coming back
1725 if (tt->tt_class == TT_REQUEST)
1727 _DtFreeRequest(data->requestPtr);
1728 XtFree(data->actionLabel);
1729 XtFree((char *)data);
1731 tttk_message_destroy(message);
1735 * The child is alive and well (so far as we can tell).
1737 actChildRecP->childState = _DtActCHILD_ALIVE;
1739 if (tt->tt_class == TT_NOTICE) {
1741 * The child is done.
1743 * By marking the child as _DtActCHILD_DONE*, see the
1744 * comment at the end of this routine that describes how
1745 * DtActionInvoke() will do a child evaluation and cleanup.
1747 actChildRecP->childState = _DtActCHILD_DONE;
1750 * Need to clean up after a notify, since there is no
1753 tttk_message_destroy(message);
1757 * Stash data away for long term usage.
1759 actChildRecP->u.tt.TTProcId = tt_message_sender( message );
1760 actChildRecP->u.tt.reqMessage = message;
1761 actChildRecP->u.tt.isTtMedia = 0;
1762 actChildRecP->u.tt.TtMediaOp = (Tttk_op) NULL;
1763 actChildRecP->u.tt.subConPats = subcon_patterns;
1769 * _DtActExecutionLeafNodeCleanup( id, (DtActionArg *) NULL, 0, 1 );
1771 * This is an execution leaf node, but because SET_INV_COMPLETE()
1772 * has NOT been called yet, doing a _DtActExecutionLeafNodeCleanup()
1773 * is useless. As we bubble back up, let DtActionInvoke() flip
1774 * SET_INV_COMPLETE and then do a _DtActExecutionLeafNodeCleanup()
1779 /******************************************************************************
1781 * This function checks to see if there is already a default ToolTalk
1782 * session active for this client. If there is one, then we will use
1783 * it for the ToolTalk message we are about to send. If there is not
1784 * a default ToolTalk session, then we will try to establish a connection
1785 * to a new session. If we succeed in connecting to ToolTalk, then we
1786 * will return 'True'; 'False' is returned if we fail.
1789 _DtInitializeToolTalk(Widget w)
1791 static int RegisteredPatterns = 0;
1795 Tt_pattern pat; /* pattern to register for exec* messages */
1797 extern Widget _DtInitTtContextWidget;
1800 * See if the context widget is set.
1802 if ( w && !_DtInitTtContextWidget )
1804 _DtInitTtContextWidget = w;
1808 /* The _Dt_context_widget should have been set in DtBigInitialize() */
1809 myassert (_DtInitTtContextWidget != NULL);
1811 if ( (status = _DtSvcInitToolTalk(_DtInitTtContextWidget)) != TT_OK )
1814 _DtSvcProcessLock();
1815 if ( !RegisteredPatterns )
1817 if ( !(session_id = tt_default_session()))
1820 * Unable to get session id -- what now?
1823 _DtSvcProcessUnlock();
1824 return(TT_ERR_NOMP);
1828 * Register to receive interesting messages from dtexec
1830 pat = tt_pattern_create();
1832 tt_pattern_category_set(pat,TT_HANDLE);
1833 tt_pattern_scope_add(pat, TT_SESSION);
1834 tt_pattern_op_add(pat,"_DtActDtexecID");
1835 tt_pattern_callback_add( pat, (Tt_message_callback) _DtActReceiveExecId);
1836 tt_pattern_session_add(pat,session_id);
1838 tt_pattern_register(pat);
1841 * Create/register a new pattern for the done callback
1843 pat = tt_pattern_create();
1845 tt_pattern_category_set(pat,TT_HANDLE);
1846 tt_pattern_scope_add(pat, TT_SESSION);
1847 tt_pattern_op_add(pat,"_DtActDtexecDone");
1848 tt_pattern_callback_add(pat, (Tt_message_callback) _DtActReceiveDoneMsg);
1849 tt_pattern_session_add(pat,session_id);
1851 tt_pattern_register(pat);
1853 tt_free(session_id);
1854 RegisteredPatterns = 1;
1857 _DtSvcProcessUnlock();
1862 /******************************************************************************
1864 * fdt: This function needs to be written!
1866 * All of the error messages generated by the tooltalk layer itself (as
1867 * opposed to those messages generated by the handler of a message) are
1868 * returned as one long message string, with no line breaks. When one of
1869 * these messages is displayed in an error dialog, the dialog stretches
1870 * across the full width of the display, and even then, some of the message
1871 * string is not visible. To remedy this, we need to 'wrap' these lines,
1872 * so that they are of a reasonable length, and we also need to expand
1873 * 'tab' characters, since these messages also use them.
1875 * tt_free()s the string passed in.
1876 * The returned character string must be tt_free()d by the caller.