ttserver: fixup forward (vexing) fucntion decl's in main, get rid of **environ
[oweals/cde.git] / cde / lib / DtSvc / DtUtil1 / ActionTt.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /* $TOG: ActionTt.c /main/12 1999/09/16 14:56:00 mgreess $ */
24 /* 
25  * (c) Copyright 1997, The Open Group 
26  */
27 /*************************************<+>*************************************
28  *****************************************************************************
29  **
30  **   File:         ActionTt.c
31  **
32  **   Project:      CDE Execution Management
33  **
34  **   Description:  This file contains the Tooltalk portions of the 
35  **                 action library source code.
36  **
37  **
38  **       by Hewlett-Packard Company
39  **
40  **
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  ************************************<+>*************************************/
47
48 /******************************************************************************
49  *
50  * TT_Message Key Assignments For Attached Data  (please note additions here!)
51  *
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
56  *
57  *****************************************************************************/
58
59 /*LINTLIBRARY*/
60 #include <stdio.h>
61 #include <errno.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <sys/param.h>
65
66 #ifdef _SUN_OS /* Need this for the strtod () call */
67 #include <floatingpoint.h>
68 #endif /* _SUN_OS */
69
70 #include <string.h>
71 #include <stdlib.h>
72 #include <limits.h>
73
74 #include <X11/Intrinsic.h>
75
76 #include <Dt/DtP.h>
77 #include <Dt/Dts.h>
78 #include <Dt/Help.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>
85 #include <Dt/SvcTT.h>
86 #include <Dt/Service.h>
87
88 #include <Dt/ActionUtilP.h>
89 #include <Dt/ActionDb.h>
90 #include <Dt/ActionFind.h>
91 #include <Tt/tttk.h>
92
93 #include <Xm/Xm.h>
94 #include <Xm/XmP.h>
95 #include <Xm/BulletinB.h>
96 #include <Xm/DialogS.h>
97 #include <Xm/Frame.h>
98 #include <Xm/Form.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>
105
106 #include <Dt/ActionP.h>
107 #include <Dt/Action.h>
108 #include <Dt/EnvControlP.h>
109
110 #include "myassertP.h"
111 #include "DtSvcLock.h"
112
113 /********    Public Function Declarations    ********/
114 void _DtProcessTtRequest(
115                         Widget w,
116                         ActionRequest *request,
117                         char * relPathHost,
118                         char * relPathDir ) ;
119
120 Tt_status _DtInitializeToolTalk(Widget w);
121
122 extern void _DtCreateErrorDialog( 
123                         Widget w,
124                         char * actionName,
125                         XmString msg) ;
126 extern Boolean _DtCompileMessagePiece(
127                         Widget w,
128                         ActionRequest *request,
129                         char * relPathHost,
130                         char * relPathDir,
131                         parsedMsg * piece,
132                         Boolean initialize,
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    ********/
141
142
143
144 /********    Static Function Declarations    ********/
145
146 static Boolean ResolveTtRequestMessagePieces(
147                         Widget w,
148                         ActionRequest *request,
149                         char * relPathHost,
150                         char * relPathDir ) ;
151
152 static void InitiateTtRequest(
153                         Widget w,
154                         ActionRequest *request ) ;
155
156 static Tt_callback_action TtRequestCallbackHandler(
157                         Tt_message message,
158                         Tt_pattern pattern ) ;
159
160 static char * WrapMessageLines( 
161                         char * errorMsg ) ;
162
163 static void ReportToolTalkError(
164                         Widget w,
165                         String actionName,
166                         String errorMsg) ;
167
168 static Tt_callback_action _DtActReceiveExecId(
169                         Tt_message msg,
170                         Tt_pattern pat );
171 static Tt_callback_action _DtActReceiveDoneMsg(
172                         Tt_message msg,
173                         Tt_pattern pat );
174 static Tt_message _DtTtContractIgnoreMsgCB(
175         Tt_message      msg,
176         void            *clientdata,
177         Tt_message      contract);
178
179 /********    End Static Function Declarations    ********/
180
181
182
183 /* Pointers to localizable strings */
184 /******************************************************************************
185  RWV:
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.
190      
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.
196
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;
215
216
217 /*****************************************************************************
218  *
219  * Routines to field message patterns from dtexec
220  *
221  *****************************************************************************/
222
223 static Tt_callback_action _DtActReceiveExecId( 
224     Tt_message msg,
225     Tt_pattern pat )
226 {
227    int invId;
228    int childId;
229    char *procId;
230    _DtActChildRecT *childp;
231
232    switch(tt_message_arg_ival(msg,0,&invId))
233    {
234        case TT_OK: /* got invocation Id (arg 0 value ) */ 
235            break;
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;
240            break;
241        default:
242            return  TT_CALLBACK_CONTINUE;
243            break;
244    }
245    switch(tt_message_arg_ival(msg,1,&childId) ) /* child Id (arg 1 value ) */ 
246    {
247        case TT_OK: /* got child Id (arg 1 value ) */ 
248            break;
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;
253            break;
254        default:
255            return  TT_CALLBACK_CONTINUE;
256            break;
257    }
258    switch ( tt_ptr_error(procId = tt_message_arg_val(msg,2)) )
259    {
260        case TT_OK: /* got dtexec's proc Id (arg 2 value ) */ 
261            break;
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;
266            break;
267        default:
268            return  TT_CALLBACK_CONTINUE;
269            break;
270    }
271    
272    /*
273     * Save the proc Id for dtexec returned in this message
274     */
275    childp = _DtActFindChildRec( invId, childId );
276    myassert(childp);  /* we should be able to find the child record */
277  
278    if ( childp )
279    {
280        /*
281         * This should be a command action
282         */
283        myassert( IS_CMD(childp->mask) );
284        childp->u.cmd.TTProcId = XtNewString( procId );
285
286        /*
287         * Note that the child HAS identified itself.
288         */
289        if (childp->childState == _DtActCHILD_ALIVE_UNKNOWN)
290            childp->childState = _DtActCHILD_ALIVE;
291    }
292
293    tt_message_reply(msg);
294    tttk_message_destroy(msg);
295    tt_free(procId);
296    return TT_CALLBACK_PROCESSED;
297 }
298
299 static Tt_callback_action _DtActReceiveDoneMsg( 
300                Tt_message msg,
301                Tt_pattern pat )
302 {
303    int              invId;
304    int              childId;
305    _DtActChildRecT *childp;
306    DtActionArg     *retArgv;    /* returnable arguments */ 
307    int              retArgc;    /* returnable argument count */
308    int              doneCode;   /* returnable _DtActCHILD_ code */
309
310    switch(tt_message_arg_ival(msg,0,&invId))
311    {
312        case TT_OK: /* got invocation Id (arg 0 value ) */ 
313            break;
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;
318            break;
319        default:
320            return  TT_CALLBACK_CONTINUE;
321            break;
322    }
323
324    switch(tt_message_arg_ival(msg,1,&childId) ) /* child Id (arg 1 value ) */ 
325    {
326        case TT_OK: /* got child Id (arg 1 value ) */ 
327            break;
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;
332            break;
333        default:
334            return  TT_CALLBACK_CONTINUE;
335            break;
336    }
337
338    switch(tt_message_arg_ival(msg,2,&doneCode) ) /* done code (arg 2 value ) */ 
339    {
340        case TT_OK: /* got done code (arg 2 value ) */ 
341            break;
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;
346            break;
347        default:
348            return  TT_CALLBACK_CONTINUE;
349            break;
350    }
351
352    /*
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.
355     */
356    tt_message_reply(msg);
357    tttk_message_destroy(msg);
358
359    childp = _DtActFindChildRec( invId, childId );
360    myassert(childp);  /* we should be able to find the child record */
361
362    /*
363     * Init return args incase there is not a childp.
364     */
365    retArgv = NULL;
366    retArgc = 0;
367
368    if (childp)
369    {
370        childp->childState = doneCode;   /* usually _DtActCHILD_DONE */
371        retArgc = _DtActGetCmdReturnArgs(invId,childp,&retArgv);
372    }
373    _DtActExecutionLeafNodeCleanup(invId, retArgv, retArgc, True);
374
375    return TT_CALLBACK_PROCESSED;
376 }
377
378
379 /******************************************************************************
380  ******************************************************************************
381  *
382  * Routine to query DtActionQuit() behavior if done now.
383  *
384  * DtActionStatus
385  * DtActionQuitType(
386  *         DtActionInvocationID    id,
387  *         int                     silent)
388  *
389  * This function has been obsoleted.  See revision
390  * number 1.6 for the implementation.
391  *
392  ******************************************************************************
393  *****************************************************************************/
394
395 /******************************************************************************
396  ******************************************************************************
397  *
398  * Routines to quit actions.
399  *
400  * static Tt_callback_action
401  * _DtActQuitCB(
402  *     Tt_message message,
403  *     Tt_pattern pattern)
404  *
405  * This function has been obsoleted.  See revision
406  * number 1.6 for the implementation.
407  *
408  ******************************************************************************
409  *****************************************************************************/
410
411 /******************************************************************************
412  *
413  * Routine used to Quit a specific action.  Note
414  * that status is returned via the childState
415  * flag.
416  *
417  * static void
418  * _DtActionQuitChild(
419  *      DtActionInvocationID   id,
420  *      _DtActChildRecT       *childRecP,
421  *      int                    silent,
422  *      int                    force,
423  *      XtAppContext           context,
424  *      int                    ms_timeout)
425  *
426  * This function has been obsoleted.  See revision
427  * number 1.6 for the implementation.
428  *
429  ******************************************************************************/
430
431 /******************************************************************************
432  *
433  * Public API
434  *
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.
438  *
439  * DtActionStatus
440  * DtActionQuit(
441  *      DtActionInvocationID    id,
442  *      unsigned long           ms_timeout,
443  *      int                     silent)
444  *
445  * This function has been obsoleted.  See revision
446  * number 1.6 for the implementation.
447  *
448  ******************************************************************************/
449
450 /******************************************************************************
451  ******************************************************************************
452  *
453  * Error Dialog Code
454  *
455  ******************************************************************************
456  *****************************************************************************/
457 static void
458 ReportToolTalkError(
459         Widget w,
460         String actionName,
461         String errorMsg )
462
463 {
464    XmString msg;
465    char * buf;
466
467    if (errorMsg)
468    {
469       buf = XtMalloc(strlen(ToolTalkErrorMsg2) + strlen(errorMsg) + 10);
470       sprintf(buf, ToolTalkErrorMsg2, errorMsg);
471    }
472    else
473       buf = XtNewString(ToolTalkErrorMsg);
474    msg = XmStringCreateLocalized(buf);
475    _DtCreateErrorDialog(w, actionName, msg);
476    XmStringFree(msg);
477    XtFree(buf);
478 }
479
480
481
482 /******************************************************************************
483  ******************************************************************************
484  *
485  * ToolTalk Specific Functions
486  *
487  ******************************************************************************
488  *****************************************************************************/
489
490 /******************************************************************************
491  *
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
503  * once.
504  */
505 void 
506 _DtProcessTtRequest(
507         Widget w,
508         ActionRequest *request,
509         char * relPathHost,
510         char * relPathDir)
511
512 {
513    ActionPtr action = request->clonedAction;
514
515    if (ResolveTtRequestMessagePieces(w, request, relPathHost, relPathDir))
516    {
517       /* 
518        * Issue the request; the success/failure notification comes 
519        * asynchronously; that's when everything gets cleaned up.
520        */
521       InitiateTtRequest( w, request );
522    }
523    else
524    {
525       /* Display error dialog */
526       /*
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]
531        */
532       /* fdt: this string must be localized */
533       ReportToolTalkError(w, request->clonedAction->label, TtFileArgMapErr);
534    }
535 }
536
537
538 /******************************************************************************
539  *
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.
543  */
544 static Boolean 
545 ResolveTtRequestMessagePieces(
546         Widget w,
547         ActionRequest *request,
548         char * relPathHost,
549         char * relPathDir )
550
551 {
552    ActionPtr action = request->clonedAction;
553    tt_msgAttr * tt = &(action->u.tt_msg);
554    int i;
555    Boolean success;
556    Boolean * paramUsed = NULL;
557    int promptDataIndex = 0;
558
559    if (_DtCompileMessagePiece(w, request, relPathHost, relPathDir,
560                            &(tt->tt_op), True, 0, &paramUsed, &promptDataIndex)
561         &&
562        _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
563                            &(tt->tt_file), False, 0, &paramUsed, &promptDataIndex))
564    {
565       for (i = 0, success = True; (i < tt->value_count) && success; i++)
566       {
567          success = _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
568                            &(tt->tt_argn_value[i]), False, _DTAct_TT_ARG, &paramUsed, &promptDataIndex);
569       }
570       for (i = 0; (i < tt->vtype_count) && success; i++)
571       {
572          success = _DtCompileMessagePiece(w, request, relPathHost, relPathDir,
573                            &(tt->tt_argn_vtype[i]), False, _DTAct_TT_VTYPE, &paramUsed, &promptDataIndex);
574       }
575
576       if (success)
577       {
578          /* We must have at least the op strings */
579          if ((tt->tt_op.compiledMessage) && 
580              (strlen(tt->tt_op.compiledMessage) > 0))
581          {
582            XtFree(paramUsed);
583            return(True);
584          }
585       }
586    }
587
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++)
594    {
595       XtFree(tt->tt_argn_value[i].compiledMessage);
596       tt->tt_argn_value[i].compiledMessage = NULL;
597    }
598    for (i = 0; i < tt->vtype_count; i++)
599    {
600       XtFree(tt->tt_argn_vtype[i].compiledMessage);
601       tt->tt_argn_vtype[i].compiledMessage = NULL;
602    }
603    XtFree(paramUsed);
604    return(False);
605 }
606
607 /******************************************************************************
608  ******************************************************************************
609  *
610  * Routines to handle Tooltalk responses.
611  *
612  ******************************************************************************
613  *****************************************************************************/
614
615 /******************************************************************************
616  *
617  * Translate a ttmedia_load() callback into a regular
618  * looking DtActionCallbackProc() for the user.
619  */
620 Tt_message
621 Ttmedia_to_Dt_StatusUpdateCB(
622     Tt_message     message,
623     void           *clientdata,
624     Tttk_op        op,
625     unsigned char  *contents,
626     int            len,
627     char           *file)
628 {
629     CallbackData *data;
630     char * errorMsg;
631     Tt_state state;
632     Tt_status status;
633     Boolean wrapMessageLines = False;
634     _DtActChildRecT *childRec;
635     _DtActInvRecT *invRec;
636     DtActionInvocationID id;
637     DtActionArg *newArgp = NULL;        /* hanger for returned data if any */
638     int  newArgc = 0;
639     unsigned long evalStatus;
640     DtActionStatus userStatus;
641     int i, j, upIdx;
642     char *upVType;
643
644
645     status = (Tt_status) tt_message_status(message);
646     state  = (Tt_state) tt_message_state(message);
647
648     if (state == TT_STARTED) {
649         /*
650          * Handler is just getting started.  Eat it.
651          */
652         tttk_message_destroy( message );
653         return( (Tt_message) NULL );           /* like TT_CALLBACK_PROCESSED */
654     }
655
656     if ( (state != TT_HANDLED) && (state != TT_FAILED) &&
657          (state != TT_RETURNED) && (state != TT_SENT)      )
658     {
659         /*
660          * This address space is probably the handler.  Pass
661          * on it, so that the handler gets a chance to handle it.
662          */
663         return( (Tt_message) message );         /* like TT_CALLBACK_CONTINUE */
664     }
665
666     /*
667      * Process a TT_FAILED, TT_HANDLED, TT_RETURNED or TT_SEND related to our
668      * original request.
669      */
670
671     /*
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.
675      *
676      * data = (CallbackData *) tt_message_user( message, 0 );   not good enough
677      */
678     data = (CallbackData *) clientdata;
679
680     id = data->actInvId;
681     invRec = _DtActFindInvRec( id );
682     myassert(invRec);
683     childRec = _DtActFindChildRec( id, data->childId );
684
685     if (state == TT_FAILED) {
686         if (status != TT_DESKTOP_ECANCELED) {
687             /*
688              * Determine whether ToolTalk or the receiver failed the message
689              */
690             if (status < TT_ERR_LAST) {
691                 errorMsg = tt_status_message(status);
692                 wrapMessageLines = True;
693             }
694             else {
695                 errorMsg = tt_message_status_string(message);
696             }
697
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)
704                     tt_free(errorMsg);
705             }
706             else {
707                 ReportToolTalkError(data->associatedWidget, 
708                                         data->actionLabel, NULL);
709             }
710         }
711     }
712     else /* if (state == TT_HANDLED) or other things (?) */ {
713         /*
714          * We have a possible update - see if user wants arg back
715          */
716
717         newArgc = invRec->ac;
718         newArgp = _DtActMallocEmptyArgArray( newArgc );
719
720         /*
721          * Our only return argument is TT_ARG0_VALUE or TT_FILE.
722          *
723          * If a docname went out, it went out as TT_IN, so we
724          * won't need to push it back up.
725          *
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.
730          */
731         i = 0;  /* arg 0 */
732         if (file == NULL) {
733             /*
734              * DtACTION_BUFFER - possible update.
735              */
736
737             /*
738              * Calculate index into original argument list from the
739              * user.
740              */
741             upIdx = childRec->argMap[i].argIdx;
742
743             if ( upIdx != -1 ) {
744                 if ( (IS_BUFFER_OBJ(invRec->info[upIdx].mask) &&
745                       IS_WRITE_OBJ(invRec->info[upIdx].mask))    ) {
746                     /*
747                      * Need to push this object up.
748                      */
749
750                     /*
751                      * Determine VTYPE
752                      */
753                     upVType = tt_message_arg_type( message, i );
754
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;
759
760                     newArgp[upIdx].u.buffer.bp = (void *) XtMalloc(len);
761                     memcpy( newArgp[upIdx].u.buffer.bp, contents, len );
762
763                     tt_free(upVType);
764                 }
765             }
766
767             /*
768              * Done with the data.
769              */
770             tt_free((char *) contents);
771         }
772         else {
773             /*
774              * DtACTION_FILE
775              */
776
777             /*
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
781              * as:
782              *      argMap[ ARG0, ... , ARGn, TT_FILE].argIdx
783              *
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
786              * will be -1 fyi.
787              *
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
791              * reasons.
792              */
793             upIdx = childRec->argMap[i+1].argIdx;
794
795             if ( upIdx != -1 ) {
796                 newArgp[upIdx].argClass = DtACTION_FILE;
797                 newArgp[upIdx].u.file.name = XtNewString( file );
798             }
799
800             tt_free(file);
801         }
802     }
803
804     /*
805      * If a final response to our original message, we're done.
806      */
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;
812         else
813             childRec->childState = _DtActCHILD_FAILED;
814     }
815
816     /*
817      * If this was a deposit, reply that we've handled it.
818      */
819     if (op == TTME_DEPOSIT) {
820         tt_message_reply( message );
821         /*
822          * Cleanup here since this message is not part of our long
823          * term storage.
824          */
825         tttk_message_destroy( message );
826     }
827
828     /*
829      * Cleanup message - handled by _DtActExecutionLeafNodeCleanup()
830      *
831      * tttk_message_destroy( message );
832      */
833
834     _DtActExecutionLeafNodeCleanup( id, newArgp, newArgc, 1 );
835
836     return( (Tt_message) NULL );        /* message consumed */
837 }
838
839
840 /******************************************************************************
841  *
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
845  * strings.
846  */
847 static int _DtAct_tt_message_arg_reptype( Tt_message message, int arg )
848 {
849
850     int       testVal;
851     Tt_status status;
852
853     status = tt_message_arg_ival( message, arg, &testVal );
854
855     if (status == TT_OK)
856         return( DtACT_TT_REP_INT);
857     else if (status == TT_ERR_NUM)
858         return( DtACT_TT_REP_BUFFER );
859 }
860
861 /******************************************************************************
862  *
863  * Translate a free-form request callback into a
864  * regular looking DtActionCallbackProc() for the user.
865  */
866 static Tt_callback_action 
867 TtRequestCallbackHandler(
868         Tt_message message,
869         Tt_pattern pattern )
870
871 {
872     CallbackData *data;
873     char * errorMsg;
874     Tt_state state;
875     Tt_status status;
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;
884     int upArgClass;
885     int argRepType;
886     char *upttbuf;
887     int upttbuflen, ivalue;
888     char *upVType, *upVType2;
889
890
891     status = (Tt_status) tt_message_status(message);
892     state  = (Tt_state) tt_message_state(message);
893
894     if (state == TT_STARTED) {
895         /*
896          * Handler is just getting started.  Eat it.
897          */
898         return TT_CALLBACK_PROCESSED;
899     }
900
901     if ((state != TT_HANDLED) && (state != TT_FAILED) && (state != TT_RETURNED))
902     {
903         /*
904          * This address space is probably the handler.  Pass
905          * on it, so that the handler gets a chance to handle it.
906          */
907         return TT_CALLBACK_CONTINUE;
908     }
909
910     /*
911      * Process a TT_FAILED, TT_HANDLED or TT_RETURNED related to our
912      * original request.
913      */
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 ); 
917     }
918
919     id = data->actInvId;
920     invRec = _DtActFindInvRec( id );
921     myassert(invRec);
922     childRec = _DtActFindChildRec( id, data->childId );
923     myassert(childRec);
924
925     if (state == TT_FAILED) {
926         if (status != TT_DESKTOP_ECANCELED) {
927             /*
928              * Determine whether ToolTalk or the receiver failed the message
929              */
930             if (status < TT_ERR_LAST) {
931                 errorMsg = tt_status_message(status);
932                 wrapMessageLines = True;
933             }
934             else {
935                 errorMsg = tt_message_status_string(message);
936             }
937
938             if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg) {
939                 if (wrapMessageLines)
940                     errorMsg = WrapMessageLines(errorMsg);
941                 ReportToolTalkError(data->associatedWidget,
942                                         data->actionLabel, errorMsg);
943                 tt_free(errorMsg);
944             }
945             else {
946                 ReportToolTalkError(data->associatedWidget, 
947                                         data->actionLabel, NULL);
948             }
949         }
950     }
951     else if (state == TT_HANDLED) {
952         /*
953          * We have an update
954          */
955         newArgc = invRec->ac;
956         newArgp = _DtActMallocEmptyArgArray( newArgc );
957
958         totalArgs = tt_message_args_count(message);
959
960         /*
961          * Look through all the arguments returned in the request.
962          */
963         for ( i = 0; i < totalArgs; i++ ) {
964             /*
965              * Look through the child's arg list to see which ones need to
966              * be returned.
967              */
968             upIdx = childRec->argMap[i].argIdx;
969
970             if ( upIdx != -1 ) {
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)        ) {
974                         /*
975                          * Need to push this object up.
976                          */
977
978                         /*
979                          * Determine argClass
980                          */
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;
987                         else
988                             upArgClass = DtACTION_NULLARG; /* error situation */
989
990                         /*
991                          * Determine VTYPE
992                          */
993                         upVType = tt_message_arg_type( message, i );
994
995                         /*
996                          * Determine REP_TYPE
997                          */
998                         argRepType = _DtAct_tt_message_arg_reptype(message, i);
999
1000                         /*
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.
1004                          */
1005                         if ( (argRepType == DtACT_TT_REP_BUFFER) &&
1006                                  !(upArgClass == DtACTION_BUFFER) )
1007                             argRepType = DtACT_TT_REP_STRING;
1008
1009                         /*
1010                          * Do appropriate unpacking.
1011                          */
1012                         switch (argRepType) {
1013                             case DtACT_TT_REP_BUFFER:
1014                             case DtACT_TT_REP_STRING:
1015                                 /*
1016                                  * By convention, if we're here, the buffer
1017                                  * is writable.   Also, STRINGs can be
1018                                  * fetched using bval.
1019                                  */
1020                                 status = tt_message_arg_bval( message, i,
1021                                          (unsigned char **) &upttbuf,
1022                                          &upttbuflen );
1023
1024                                 if ( status != TT_OK ) {
1025                                     /*
1026                                      * Give up - something bad happened.
1027                                      */
1028                                     upArgClass = DtACTION_NULLARG;
1029                                 }
1030                                 break;
1031                             case DtACT_TT_REP_INT:
1032                                 /*
1033                                  * Unpack an integer.
1034                                  */
1035                                 status = tt_message_arg_ival( message, i,
1036                                                                  &ivalue );
1037                                 if ( status != TT_OK ) {
1038                                     upArgClass = DtACTION_NULLARG;
1039                                 }
1040                                 else {
1041                                     /*
1042                                      * Fake up enough information that we
1043                                      * could return the argument in either
1044                                      * a DtACTION_FILE or DtACTION_BUFFER
1045                                      * argClass argument.
1046                                      */
1047                                     upttbuflen = 64;
1048                                     upttbuf = XtMalloc( upttbuflen );
1049                                     sprintf( upttbuf, "%u", ivalue );
1050                                 }
1051                                 break;
1052                             case DtACT_TT_REP_UNDEFINED:
1053                                 /*
1054                                  * Cannot figure out how to unpack arg.
1055                                  */
1056                                 upArgClass = DtACTION_NULLARG;
1057                                 break;
1058                         }
1059
1060                         switch (upArgClass) {
1061                             case DtACTION_NULLARG:
1062                                 /*
1063                                  * We failed above, so give up.
1064                                  */
1065                                 break;
1066                             case DtACTION_BUFFER:
1067                                 /*
1068                                  * Convert from tt_free()-able to
1069                                  * XtFree()-able.
1070                                  */
1071                                 newArgp[upIdx].argClass = upArgClass;
1072                                 newArgp[upIdx].u.buffer.size = upttbuflen;
1073                                 newArgp[upIdx].u.buffer.type = XtNewString(
1074                                                                 upVType);
1075                                 newArgp[upIdx].u.buffer.writable = 1;
1076
1077                                 newArgp[upIdx].u.buffer.bp = (void *)
1078                                                 XtMalloc( upttbuflen );
1079                                 memcpy( newArgp[upIdx].u.buffer.bp, upttbuf,
1080                                                 upttbuflen );
1081
1082                                 if (argRepType == DtACT_TT_REP_INT) {
1083                                     /*
1084                                      * upttbuf was XtMalloc'ed above
1085                                      */
1086                                     XtFree( upttbuf );
1087                                     upttbuf = 0;
1088                                 }
1089                                 break;
1090                             case DtACTION_FILE:
1091                                 /*
1092                                  * Convert from tt_free()-able to
1093                                  * XtFree()-able.
1094                                  */
1095                                 newArgp[upIdx].argClass = upArgClass;
1096                                 newArgp[upIdx].u.file.name = XtNewString(
1097                                                 upttbuf );
1098
1099                                 if (argRepType == DtACT_TT_REP_INT) {
1100                                     /*
1101                                      * upttbuf was XtMalloc'ed above
1102                                      */
1103                                     XtFree( upttbuf );
1104                                     upttbuf = 0;
1105                                 }
1106                                 break;
1107                         }
1108                     }
1109                     tt_free( upVType );
1110                     if (upttbuf != 0) {
1111                         tt_free( upttbuf );
1112                     }
1113             }
1114         }
1115     }
1116
1117     /*
1118      * If a final response to our original message, we're done.
1119      */
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;
1125         else
1126             childRec->childState = _DtActCHILD_FAILED;
1127     }
1128
1129     /*
1130      * Message is destroyed by _DtActExecutionLeafNodeCleanup()
1131      */
1132
1133     _DtActExecutionLeafNodeCleanup( id, newArgp, newArgc, 1 );
1134
1135     return( (Tt_callback_action) TT_CALLBACK_PROCESSED ); /* message consumed */
1136 }
1137
1138 /******************************************************************************
1139  ******************************************************************************
1140  *
1141  * Process a ToolTalk request.
1142  *
1143  ******************************************************************************
1144  *****************************************************************************/
1145
1146 /******************************************************************************
1147  *
1148  * _DtTtContractIgnoreMsgCB
1149  *
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
1154  *      system console.
1155  ******************************************************************************/
1156
1157 static Tt_message
1158 _DtTtContractIgnoreMsgCB(
1159         Tt_message      msg,
1160         void            *clientdata,
1161         Tt_message      contract)
1162 {
1163     /*
1164      * Artificially consume unwanted messages.
1165      */
1166     tttk_message_abandon(msg);
1167     return NULL;
1168 }
1169
1170
1171 static void 
1172 InitiateTtRequest(
1173         Widget         w,
1174         ActionRequest  *request )
1175
1176 {
1177     CallbackData *data;
1178     ActionPtr action = request->clonedAction;
1179     tt_msgAttr * tt = &(action->u.tt_msg);
1180     int i;
1181     char * p;
1182     Tt_message message;
1183     int destroy_message = 0;
1184     int mode;
1185     int repType;
1186     int intValue;
1187     char * value;
1188     char * vtype;
1189     char * errorMsg;
1190     Tt_status status;
1191     unsigned char * contents;
1192     int length;
1193     char * file;
1194     char * docname;
1195     Tt_pattern *subcon_patterns;
1196     _DtActInvRecT *actInvRecP;
1197     _DtActChildRecT *actChildRecP;
1198     unsigned long evalStatus;
1199     DtActionStatus userStatus;
1200     int j;
1201     char *bufFilename = NULL;
1202     int ttmedia_test;
1203
1204
1205     /*
1206      * Find associated long term records for this request
1207      */
1208     actInvRecP   = _DtActFindInvRec( request->invocId );
1209     myassert(actInvRecP);
1210     actChildRecP = _DtActFindChildRec( request->invocId, request->childId );
1211     myassert(actChildRecP);
1212
1213     actChildRecP->childState = _DtActCHILD_UNKNOWN;     /* really don't know */
1214
1215     /*
1216      * Future:
1217      *
1218      * Consider doing a check of argClass -vs- TT_ARGn_REP_TYPE,
1219      * and issue errors where appropriate.
1220      *
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
1226      *
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
1232      */
1233
1234     /*
1235      * Test request for ttmedia_load() suitability.
1236      *
1237      * To be ttmedia_load() suitable, action defs must look like:
1238      *
1239      * Action TtmediaBufferStyle
1240      * {
1241      *     ARG_CLASS         BUFFER
1242      *     ARG_COUNT         <3
1243      *
1244      *     TYPE              TT_MSG
1245      *     TT_OPERATION      <op>
1246      *     TT_CLASS          TT_REQUEST
1247      *     TT_SCOPE          TT_SESSION
1248      *     TT_FILE           --- must be null or unset ---
1249      *
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%
1254      *
1255      *     optional TT_ARG1 for docname
1256      *
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%
1261      * }
1262      * 
1263      * 
1264      * Action TtmediaFileStyle
1265      * {
1266      *     ARG_CLASS         FILE
1267      *     ARG_COUNT         <3
1268      *
1269      *     TYPE              TT_MSG
1270      *     TT_OPERATION      <op>
1271      *     TT_CLASS          TT_REQUEST
1272      *     TT_SCOPE          TT_SESSION
1273      *     TT_FILE           <filename> | %(File)Arg_n%
1274      *
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 ---
1279      *
1280      *     optional TT_ARG1 for docname
1281      *
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%
1286      * }
1287      */
1288
1289     ttmedia_test = 0;   /* default to free-form */
1290
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) ) {
1295
1296         /*
1297          * 1- and 2-arg session-scoped requests not using ivals
1298          * can be sent via ttmedia_load()...
1299          */
1300             ttmedia_test = 1;
1301
1302         /*
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.
1307          */
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" )) ) {
1312                 ttmedia_test = 0;
1313             }
1314         }
1315     }
1316
1317     if (ttmedia_test)
1318     {
1319         /**********************************************************************
1320          *
1321          * TT_MEDIA_LOAD()
1322          *
1323          * Special case media-load requests *and* any other requests that
1324          * follow the "ttmedia style".
1325          *
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.
1328          *
1329          * Using ttmedia_load() sets up additional machinery.
1330          *
1331          */
1332
1333         Tttk_op op = tttk_string_op(tt->tt_op.compiledMessage);
1334
1335         /*
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.
1338          */
1339         switch (tt->tt_argn_mode[0]) {
1340             case TT_IN:
1341                 op = TTME_DISPLAY;
1342                 break;
1343             case TT_OUT:
1344                 op = TTME_COMPOSE;
1345                 break;
1346             case TT_INOUT:
1347                 op = TTME_EDIT;
1348                 break;
1349         }
1350
1351         contents = (unsigned char *) NULL;
1352         length   = (int)             0;
1353         /*
1354          * Determine what the data looks like: file or a buffer?
1355          */
1356         if (   tt->tt_argn_rep_type[0] == DtACT_TT_REP_BUFFER
1357             || tt->tt_argn_rep_type[0] == DtACT_TT_REP_UNDEFINED)
1358         {
1359             /*
1360              * Passing a buffer of data.
1361              */
1362
1363             /*
1364              * See if we should pluck the buffer out of a tmp file.
1365              */
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;
1374                 }
1375             }
1376         }
1377
1378         if (bufFilename) {
1379             contents = (unsigned char *)
1380                             _DtActReadTmpFileToBuffer(bufFilename, &length);
1381             /* length plucked from above */
1382         }
1383         else {
1384             contents = (unsigned char *)
1385                             tt->tt_argn_value[0].compiledMessage;
1386             length = tt->tt_argn_value[0].msgLen;
1387         }
1388
1389         file     = (char *) tt->tt_file.compiledMessage;
1390         if (tt->mode_count == 2)
1391             docname  = (char *) tt->tt_argn_value[1].compiledMessage;
1392         else
1393             docname  = (char *) NULL;
1394
1395         /*
1396          * Fill out a callback structure.
1397          */
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;
1405
1406         /*
1407          * Let tttk build the message and setup additional machinery.
1408          */
1409         message = ttmedia_load( (Tt_message) 0,         /* Am top level req */
1410                                 Ttmedia_to_Dt_StatusUpdateCB,
1411                                 (void *) data,
1412                                 op,
1413                                 tt->tt_argn_vtype[0].compiledMessage,
1414                                 contents,
1415                                 length,
1416                                 file,
1417                                 docname,
1418                                 FALSE );
1419         status = tt_ptr_error(message);
1420         if (! tt_is_err(status))
1421         {
1422             /*
1423              * (Re)set the op.  This allows arbitrary Media-like
1424              * TT_MSG actions to use the Media action machinery.
1425              */
1426             tt_message_op_set(message, tt->tt_op.compiledMessage);
1427
1428             /*
1429              * Attach client data to message using slot 0.
1430              */
1431             tt_message_user_set(message, 0, (void *) data);
1432
1433             actChildRecP->childState = _DtActCHILD_PENDING_START; /* starting */
1434
1435             /*
1436              * I'm assuming w is non-NULL, hence the lack of a callback
1437              * and callback data.
1438              */
1439             subcon_patterns = ttdt_subcontract_manage(message,
1440                                 (Ttdt_contract_cb) _DtTtContractIgnoreMsgCB,
1441                                 w, (void *) NULL);
1442
1443             status = tt_message_send(message);
1444             destroy_message = 1;
1445         }
1446    
1447         /*
1448          * See if we failed
1449          */
1450         if (status != TT_OK) {
1451             /*
1452              * The child error'ed out.
1453              */
1454             actChildRecP->childState = _DtActCHILD_FAILED;
1455
1456             /*
1457              * Report the fact that the request failed
1458              */
1459             errorMsg = tt_status_message(status);
1460             if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg)
1461             {
1462                 errorMsg = WrapMessageLines(errorMsg);
1463                 ReportToolTalkError(w, request->clonedAction->label, errorMsg);
1464                 tt_free(errorMsg);
1465             }
1466             else
1467                 ReportToolTalkError(w, request->clonedAction->label, NULL);
1468    
1469             /*
1470              * Free information since no callbacks will be triggered:
1471              *    - Callback Client Data
1472              *    - The message
1473              */
1474             XtFree(data->actionLabel);
1475             _DtFreeRequest(data->requestPtr);
1476             XtFree((char *)data);
1477
1478             if (destroy_message)
1479               tttk_message_destroy(message);
1480         }
1481         else {
1482             /*
1483              * The child is alive and well (so far as we can tell).
1484              */
1485             actChildRecP->childState = _DtActCHILD_ALIVE;
1486
1487             /*
1488              * Stash data away for long term usage.
1489              */
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;
1495         }
1496
1497         /*
1498          * Throw away transition buffer if there was one.
1499          */
1500         if (bufFilename)
1501             XtFree((char *) contents);
1502    }
1503    else
1504    {
1505         /**********************************************************************
1506          *
1507          * FREE FORM TT_MSG
1508          *
1509          * All other Requests and Notices go out "free form".  Do best case.
1510          */
1511         message = tttk_message_create( 0, tt->tt_class, tt->tt_scope, 0,
1512                                         tt->tt_op.compiledMessage,
1513                                         TtRequestCallbackHandler );
1514
1515         tt_message_file_set(message, tt->tt_file.compiledMessage);
1516
1517         /*
1518          * Process each of the message arguments
1519          *
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.
1523          */
1524         for (i = 0; i < tt->mode_count; i++)
1525         {
1526             /*
1527              * Pluck some data out of the argMap to help us make
1528              * some decisions here.
1529              */
1530             length = -1;                        /* -1 means not a buffer */
1531             bufFilename = (char *) NULL;        /* possible buffer tmp file */
1532
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;
1540                 }
1541             }
1542
1543             /*
1544              * Determine MODE
1545              */
1546             mode = tt->tt_argn_mode[i];
1547    
1548             /*
1549              * Determine REP TYPE
1550              */
1551             if ((i < tt->rep_type_count) && 
1552                 (tt->tt_argn_rep_type[i] != DtACT_TT_REP_UNDEFINED))
1553             {    
1554                 /* The representation type was supplied; use it */
1555                 repType = tt->tt_argn_rep_type[i];
1556             }
1557             else
1558             {
1559                 /*
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.
1563                  */
1564                 if ( length != -1 )
1565                     repType = DtACT_TT_REP_BUFFER;
1566                 else
1567                     repType = DtACT_TT_REP_STRING;
1568             }
1569
1570             /*
1571              * Determine VALUE and VTYPE
1572              */
1573             if (i < tt->value_count)
1574             {
1575                 /*
1576                  * Use the specified vtype
1577                  */
1578                 vtype = tt->tt_argn_vtype[i].compiledMessage;
1579
1580                 /*
1581                  * Determine where to get the value from
1582                  */
1583                 if ( length != -1 ) {
1584                     /*
1585                      * Have a buffer - see if it's value can be fetched
1586                      * from a tmp file.   If so, will need to free later!
1587                      */
1588                     if (bufFilename) {
1589                         value = _DtActReadTmpFileToBuffer(bufFilename, &length);
1590                     }
1591                     else {
1592                         value = tt->tt_argn_value[i].compiledMessage;
1593                     }
1594                 }
1595                 else {
1596                     value = tt->tt_argn_value[i].compiledMessage;
1597                 }
1598             }
1599             else
1600             {
1601                 /* No value specified; use 'NULL' */
1602                 value = NULL;
1603    
1604                 /*
1605                  * Use the specified vtype setting, if supplied; else
1606                  * default it
1607                  */
1608                 if (i < tt->vtype_count)
1609                     vtype = tt->tt_argn_vtype[i].compiledMessage;
1610                 else
1611                 {
1612                     /*
1613                      * Time to guess what the vtype should be.  This
1614                      * is very funky.
1615                      */
1616                     if (repType == DtACT_TT_REP_INT)
1617                         vtype = "integer";
1618                     else if (repType == DtACT_TT_REP_STRING)
1619                         vtype = "string";
1620                     else if (repType == DtACT_TT_REP_BUFFER)
1621                         vtype = "buffer";
1622                 }
1623             }
1624
1625             /*
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.
1629              */
1630             if (repType == DtACT_TT_REP_INT)
1631             {
1632                 if (value)
1633                     intValue = strtol(value, &p, 0);
1634                 else
1635                     intValue = 0;
1636
1637                 tt_message_iarg_add(message, mode, vtype, intValue);
1638             }
1639             else if (repType == DtACT_TT_REP_STRING)
1640             {
1641                 tt_message_arg_add(message, mode, vtype, value);
1642             }
1643             else if (repType == DtACT_TT_REP_BUFFER)
1644             {
1645                 tt_message_barg_add(message, mode, vtype,
1646                                         (unsigned char *) value, length);
1647             }
1648
1649             if ( (length != -1) && (bufFilename) ) {
1650                 /*
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.
1654                  */
1655                 XtFree(value);
1656             }
1657         }
1658
1659         if (tt->tt_class == TT_REQUEST)
1660         {
1661             /*
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.
1666              */
1667    
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;
1676    
1677             /*
1678              * Attach client data to message using slot 0.
1679              */
1680             tt_message_user_set(message, 0, (void *) data);
1681    
1682             tt_message_callback_add(message, TtRequestCallbackHandler);
1683         }
1684    
1685         actChildRecP->childState = _DtActCHILD_PENDING_START;   /* starting */
1686
1687         /*
1688          * I'm assuming w is non-NULL, hence the lack of a callback
1689          * and callback data.
1690          */
1691         subcon_patterns = ttdt_subcontract_manage(message,
1692                         (Ttdt_contract_cb) _DtTtContractIgnoreMsgCB,
1693                         w, (void *) NULL);
1694
1695         status = tt_message_send(message);
1696    
1697         /* See if we failed right off the bat */
1698         if (status != TT_OK)
1699         {
1700             /*
1701              * The child error'ed out.
1702              *
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.
1706              */
1707             actChildRecP->childState = _DtActCHILD_FAILED;
1708
1709             /*
1710              * Report the fact that the request failed
1711              */
1712             errorMsg = tt_status_message(status);
1713             if ((tt_pointer_error(errorMsg) == TT_OK) && errorMsg)
1714             {
1715                 errorMsg = WrapMessageLines(errorMsg);
1716                 ReportToolTalkError(w, request->clonedAction->label, errorMsg);
1717                 tt_free(errorMsg);
1718             }
1719             else
1720                 ReportToolTalkError(w, request->clonedAction->label, NULL);
1721    
1722             /*
1723              * Clean up now, cause there will not be a reply coming back
1724              */
1725             if (tt->tt_class == TT_REQUEST)
1726             {
1727                 _DtFreeRequest(data->requestPtr);
1728                 XtFree(data->actionLabel);
1729                 XtFree((char *)data);
1730             }
1731             tttk_message_destroy(message);
1732         }
1733         else {
1734             /*
1735              * The child is alive and well (so far as we can tell).
1736              */
1737             actChildRecP->childState = _DtActCHILD_ALIVE;
1738
1739             if (tt->tt_class == TT_NOTICE) {
1740                 /*
1741                  * The child is done.
1742                  *
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.
1746                  */
1747                 actChildRecP->childState = _DtActCHILD_DONE;
1748
1749                 /*
1750                  * Need to clean up after a notify, since there is no
1751                  * response.
1752                  */
1753                 tttk_message_destroy(message);
1754             }
1755             else {
1756                 /*
1757                  * Stash data away for long term usage.
1758                  */
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;
1764             }
1765         }
1766     }
1767
1768     /*
1769      * _DtActExecutionLeafNodeCleanup( id, (DtActionArg *) NULL, 0, 1 );
1770      *
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()
1775      * via a timer.
1776      */
1777 }
1778
1779 /******************************************************************************
1780  *
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.
1787  */
1788 Tt_status
1789 _DtInitializeToolTalk(Widget w)
1790 {
1791    static int RegisteredPatterns = 0;
1792    char * procid;
1793    Tt_status status;
1794    int fd;
1795    Tt_pattern pat;      /* pattern to register for exec*  messages */
1796    char *session_id;
1797    extern Widget _DtInitTtContextWidget;
1798
1799    /*
1800     * See if the context widget is set.
1801     */
1802    if ( w && !_DtInitTtContextWidget )
1803    {
1804         _DtInitTtContextWidget = w;
1805    }
1806
1807
1808    /* The _Dt_context_widget should have been set in DtBigInitialize() */
1809    myassert (_DtInitTtContextWidget != NULL);
1810
1811    if ( (status = _DtSvcInitToolTalk(_DtInitTtContextWidget)) != TT_OK )
1812         return status;
1813
1814    _DtSvcProcessLock();
1815    if ( !RegisteredPatterns )
1816    {
1817       if ( !(session_id = tt_default_session()))
1818       {
1819           /*
1820            * Unable to get session id -- what now?
1821            */
1822           myassert(0);
1823           _DtSvcProcessUnlock();
1824           return(TT_ERR_NOMP);
1825       }
1826
1827       /*
1828        * Register to receive interesting messages from dtexec
1829        */
1830       pat = tt_pattern_create();
1831
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);
1837
1838       tt_pattern_register(pat);
1839
1840       /*
1841        * Create/register a new pattern for the done callback
1842        */
1843       pat = tt_pattern_create();
1844     
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);
1850
1851       tt_pattern_register(pat);
1852
1853       tt_free(session_id);
1854       RegisteredPatterns = 1;
1855    }
1856
1857    _DtSvcProcessUnlock();      
1858    return (TT_OK);
1859 }
1860
1861
1862 /******************************************************************************
1863  *
1864  * fdt: This function needs to be written!
1865  *
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.
1874  *
1875  * tt_free()s the string passed in.
1876  * The returned character string must be tt_free()d by the caller.
1877  */
1878
1879 static char *
1880 WrapMessageLines( 
1881    char * errorMsg )
1882 {
1883    return errorMsg;
1884 }
1885