DtSvc/DtUtil1: fix implicit function declarations
[oweals/cde.git] / cde / lib / DtSvc / DtUtil1 / ActionUtil.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: ActionUtil.c /main/14 1999/02/19 13:10:09 mgreess $ */
24 /*************************************<+>*************************************
25 *****************************************************************************
26 **
27 **   File:         ActionUtil.c
28 **
29 **   Project:       DT
30 **
31 **   Description:  This file contains the action library utility source code.
32 **                
33 ** (c) Copyright 1993, 1994 Hewlett-Packard Company
34 ** (c) Copyright 1993, 1994 International Business Machines Corp.
35 ** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
36 ** (c) Copyright 1993, 1994 Novell, Inc.
37 ****************************************************************************
38 ************************************<+>*************************************/
39
40 /*LINTLIBRARY*/
41 #include <ctype.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/param.h>  /* for *MAX* macros */
48 #define X_INCLUDE_NETDB_H
49 #define X_INCLUDE_GRP_H
50 #define XOS_USE_XT_LOCKING
51 #include <X11/Xos_r.h>
52
53 #ifdef _SUN_OS /* Need this for the strtod () call */
54 #include <floatingpoint.h>
55 #endif /* _SUN_OS */
56
57 #include <string.h>
58 #include <stdlib.h>
59 #include <limits.h>
60
61
62 #include <X11/Xlib.h>   /* for DisplayString */
63 #include <X11/Xresource.h>      /* for X resource defs */
64 #include <X11/Intrinsic.h>      /* for X toolkit functions */
65
66 #include <Dt/ActionP.h>
67 #include <Dt/ActionUtilP.h>
68 #include <Dt/CmdInvP.h>
69 #include <Dt/DtNlUtils.h>
70 #include <Dt/Indicator.h>
71
72 #include "myassertP.h"
73 #include "DtSvcLock.h"
74
75 /******************************************************************************
76  *
77  * List of active DtActionInvoke() DtActionInvocationID's
78  *
79  *****************************************************************************/
80
81 static _DtActInvRecT    **_DtActInvRecArray;            /* top of array */
82 static int              _actInvRecArraySize;            /* size of array */
83
84 static void     _DtActFreeInvRec();                     /* free an inv rec */   
85 static void      _DtActFreeChildRec( _DtActChildRecT *childp);
86
87 #ifndef NDEBUG
88         /* I want to exercise the realloc code for now */
89 #define                 _START_INVREC_SIZE 1            /* initial array size */
90 #else
91 #define                 _START_INVREC_SIZE 32           /* initial array size */
92 #endif  /* NDEBUG */
93
94 #ifndef P_tmpdir
95 #define P_tmpdir        "/var/tmp"
96 #endif
97
98 extern void _DtFreeRequest(ActionRequest *request );
99
100
101 /*******************************************************************************
102  *  _DtBasename -- utiltiy function to return a pointer to a
103  * string containing the basename of a file name.  It doesn't
104  * modifiy the original string parameter. If the original file
105  * path ends in "/" the last component of the path is returned as
106  * the basename.
107  ******************************************************************************/
108 char *
109 _DtBasename( const char *s ) 
110 {
111         char *basep;
112         char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1];
113         char *p = buf;
114
115
116         if (!s)
117                 return NULL;
118
119         /* Work on a local copy of the original string */
120         *p = '\0';
121         (void)strcpy(p,s);
122
123 #ifdef _Dt_HOST_COLON_PATH_SUPPORT
124         /* Chop off the "host:" if necessary */
125         if ( basep = DtStrchr(p,'/') )
126         {
127                 /* if ( basep > p && (*(basep - 1) == ':') ) */
128                 if ( basep > p && ( *DtPrevChar(p,basep) == ':') )
129                 p = basep;
130         }
131 #endif  /* _Dt_HOST_COLON_PATH_SUPPORT */
132
133         if ( (basep = DtStrrchr(p,'/')) == NULL )
134                 return XtNewString(p);
135
136         
137         if (( basep == p) && (strlen(basep) == 1))
138                 return XtNewString("/");
139         
140         /*
141          * check for trailing slash
142          */
143         /* while (basep == p+strlen(p)-1 ) */
144         while (basep == DtPrevChar(p,p+strlen(p)) )
145         {
146                 myassert( *basep == '/');
147                 if ( basep == p )
148                         return XtNewString(basep);
149                 *basep = '\0';  /* replace trailing slash */
150                 if ( (basep = DtStrrchr(p,'/')) == NULL )
151                         return XtNewString(p);
152         }
153
154         /* skip past '/' before returning basename */
155         basep++;
156         return XtNewString(basep);
157
158 }
159
160
161 /*******************************************************************************
162  * _DtPathString -- returns the path portion of the "host:/path" string
163  * passed in as a parameter.  
164  * The incoming string is assumed to be in : "[host:]/path format.
165  * NOTE: a FULL path name is required.
166  ******************************************************************************/
167 char *
168 _DtPathname( const char *s)
169 {
170         char *slashp;
171
172         if ( !s )
173         {
174                 myassert(0);
175                 return NULL;
176         }
177
178         /* Chop off the "host:" if necessary */
179         slashp = DtStrchr((char *)s,'/'); 
180
181         /* if ( slashp > s && (*(slashp - 1) != ':') ) */
182         if ( slashp > s && (*DtPrevChar(s,slashp) != ':') )
183         {
184                 /* 
185                  *  full path name required
186                  * --- should never get here
187                  */
188                 myassert(0);
189                 return NULL;
190         }
191
192         return XtNewString(slashp);
193 }
194
195 /*******************************************************************************
196  * _DtDirname -- returns the directory portion of the file
197  * path string passed in as a parameter.  The original string
198  * may be modified to remove trailing slashes.  
199  * The incoming string is assumed to be in : "[host:/]dir/file" format.
200  ******************************************************************************/
201 char *
202 _DtDirname( const char *s)
203 {
204         char *slashp;
205         char *dirp;
206         char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1];
207         char *p = buf;;
208
209
210         /* Work on a local copy of the original string */
211         *p = '\0';
212         (void)strcpy(p,s);
213
214 #ifdef _Dt_HOST_COLON_PATH_SUPPORT
215         /* Chop off the "host:" if necessary */
216         if ( slashp = DtStrchr(p,'/') )
217         {
218                 /* if ( slashp > p && (*(slashp - 1) == ':') ) */
219                 if ( slashp > p && (*DtPrevChar(p,slashp) == ':') )
220                 p = slashp;
221         }
222 #endif  /* _Dt_HOST_COLON_PATH_SUPPORT */
223
224         /* handle multiple trailing slashes */
225
226         while ( (slashp = DtStrrchr(p,'/')) )
227         {
228
229                 /*
230                  * Special case '/' -- return '/'
231                  */
232                 if ( slashp == p ) 
233                         return XtNewString("/");
234
235                 /*
236                  * Is this a trailing slash ?
237                  * -- then try again else break
238                  */
239                 if ( slashp == DtPrevChar(p,p + strlen(p)) )
240                         *slashp = '\0';
241                 else
242                         break;
243         }
244         /* malformed path */
245         if (!slashp )
246                 return NULL;
247
248         /*
249          * Replace the last '/' with a NULL to get the 
250          * directory name.
251          */
252         dirp = XtNewString(p);
253         *(dirp + (slashp - p)) = '\0';
254         return dirp;
255 }
256
257 /*****************************************************************************
258  *
259  * _DtHostString
260  *      Extract the host name string from files in the [host:][/][path/]file
261  *      format.  This function mallocs space for a new copy of the host
262  *      name string; it is up to the caller to free this space.
263  *              This function requires that names which include the hoststring
264  *      use a "full path" name to specify the file location.  If the hoststring
265  *      is omitted the path may be relative.
266  *
267  *      RETURN
268  *              If a host name string can be found; a pointer to a newly
269  *              malloced copy of the host name string is returned; otherwise
270  *              a NULL pointer is returned.
271  *
272  *      NOTE:  This function should NOT be used to extract host name strings
273  *             from display variables.
274  *      
275  *****************************************************************************/
276
277 char *
278 _DtHostString( const char *s)
279 {
280         char *slashp;
281         char *host;
282         char buf[MAXPATHLEN + MAXHOSTNAMELEN + 1];
283         char *p= buf;
284
285         if ( (slashp = DtStrchr((char *)s,'/')) == NULL )
286                 return NULL;
287
288         /*
289          * Make a local copy of the string to avoid problems modifying
290          * "const" strings.
291          */
292         *p = '\0';
293         (void) strcpy(p,s);
294
295         /* if ( (slashp > s) && (*(slashp -1) == ':' )) */
296         if ( (slashp > s) && (*DtPrevChar(s,slashp)  == ':' ))
297         {
298                 /* *(p + (slashp - s - 1)) = NULL; */
299                 *(p + (DtPrevChar(s,slashp) - s)) = '\0';
300                 host = XtNewString(p);
301                 return host;
302         }
303
304         return NULL;
305 }
306
307
308 char *
309 _DtGetSessionHostName( void )
310 {
311         static char *sessionHostName = NULL;
312
313         _DtSvcProcessLock();
314         if ( sessionHostName && *sessionHostName ) {
315                 _DtSvcProcessUnlock();
316                 return XtNewString(sessionHostName);
317               }
318
319         sessionHostName =  getenv(ENV_SESSION_SVR);
320
321         if ( sessionHostName && *sessionHostName ) {
322                 _DtSvcProcessUnlock();
323                 return  XtNewString(sessionHostName);
324               }
325
326         /*
327          * Default to display host name if the session host environment
328          * variable is not set or set to null.  The command invoker
329          * initialization should have tucked away the display name.
330          */
331
332         sessionHostName = _DtGetDisplayHostName( (Display *) NULL );
333         _DtSvcProcessUnlock();
334         return XtNewString(sessionHostName);
335
336 }
337
338 char *
339 _DtGetDisplayHostName( Display *dp)
340 {
341         static char *displayHostName = NULL;
342         char *tmpName = NULL;
343         char *tmp;
344         
345         _DtSvcProcessLock();
346
347         if ( displayHostName && *displayHostName ) {
348                 _DtSvcProcessUnlock();
349                 return XtNewString(displayHostName); 
350               }
351
352         tmpName = XtMalloc(MAXHOSTNAMELEN + 5 + 1);
353         tmpName[0] = '\0';
354
355         if ( dp )
356         {
357                 /*
358                  * If a display pointer has been provided use it to determine
359                  * display host name.
360                  */
361                 
362                 strcpy(tmpName,DisplayString(dp));
363                 if (( tmp = DtStrrchr(tmpName,':') ))
364                 {
365                         *tmp = '\0';
366                         displayHostName = XtNewString(tmpName);
367                 }
368         } 
369         else
370         {
371                 /*
372                  * As a last resort,
373                  * In the absence of a display pointer, use the
374                  * DISPLAY environment variable.
375                  */
376                 memset(tmpName, 0, (MAXHOSTNAMELEN + 5) + 1);
377                 strncpy(tmpName, getenv("DISPLAY"), (MAXHOSTNAMELEN + 5));
378                 if (( tmp = DtStrrchr(tmpName,':') ))
379                 {
380                         *tmp = '\0';
381                         displayHostName = XtNewString(tmpName);
382                 }
383         }
384         XtFree(tmpName);
385
386         /*
387          * Check for degenerate forms of the display name
388          */
389         if ( !(  displayHostName &&
390               *displayHostName &&
391               strcmp(displayHostName,"local") &&
392               strcmp(displayHostName, "unix") ))
393         {
394             /*
395              * default to localHostName
396              */
397              if (displayHostName)
398                     XtFree(displayHostName);
399              displayHostName = _DtGetLocalHostName();
400         }
401
402         myassert( displayHostName && *displayHostName );
403         myassert( (DtStrchr( displayHostName, ':' ) == NULL) );
404
405         _DtSvcProcessUnlock();
406         return XtNewString(displayHostName); 
407 }
408
409 /******************************************************************************
410  *
411  * _DtGetLocalHostname:
412  *      return the short form of the local host name.
413  *      (i.e. truncate the hostname at the first '.' character).
414  ******************************************************************************/
415
416 char *
417 _DtGetLocalHostName( void )
418 {
419         static char *localHostName = NULL;
420         static char hostNameBuf[MAXHOSTNAMELEN + 1];
421         char *ptr;
422
423         _DtSvcProcessLock();
424         if ( localHostName && *localHostName ) {
425                 _DtSvcProcessUnlock();
426                 return XtNewString(localHostName);
427               }
428
429         if ( gethostname(hostNameBuf, sizeof(hostNameBuf)) ) {
430                 _DtSvcProcessUnlock();
431                 return NULL; /* failed gethostname */
432               }
433         if ((ptr = DtStrchr(hostNameBuf, '.')))
434                 *ptr = '\0';  /* delete domain name if there is one */
435
436         localHostName = hostNameBuf;
437         _DtSvcProcessUnlock();
438         return XtNewString(localHostName);
439 }
440
441 /******************************************************************************
442  *
443  * _DtIsSameHost
444  *      return True if the two host names provided are actually references to
445  *      the same host; False otherwise.  If either host1 or host2 is a NULL
446  *      pointer use the "local host" name.
447  *
448  ******************************************************************************/
449
450
451 int
452 _DtIsSameHost(const char *host1, const char *host2)
453 {
454         char hostName1[MAXHOSTNAMELEN + 1];
455         char hostName2[MAXHOSTNAMELEN + 1];
456         struct hostent          *host_ret;
457         _Xgethostbynameparams   host_buf;
458         char *tp;
459
460         /*
461          * If either parameter is null; use the local host name in its stead
462          */
463         if ( !host1 )
464         {
465                 tp = _DtGetLocalHostName();
466                 strcpy(hostName1,tp);
467                 XtFree(tp);
468         }
469         else
470         {
471                 strcpy(hostName1,host1);
472         }
473
474         if ( !host2)
475         {
476                 tp = _DtGetLocalHostName();
477                 strcpy(hostName2,tp);
478                 XtFree(tp);
479         }
480         else
481         {
482                 strcpy(hostName2,host2);
483         }
484
485         /*
486          * We now have local copies of the hostnames in the
487          * arrays hostName1 and hostName2.  Truncate the names
488          * to their short form before doing the compare.
489          */
490         if ( (tp = DtStrchr(hostName1,'.')) != NULL )
491                 *tp = '\0';
492         if ( (tp = DtStrchr(hostName2,'.')) != NULL )
493                 *tp = '\0';
494
495         /*
496          * Try to avoid querying the name server (or /etc/hosts).
497          * Do the name strings match?
498          */
499
500         if ( !strcmp(hostName1,hostName2) )
501                 return True;
502
503         if ( (host_ret = _XGethostbyname(hostName1, host_buf)) == NULL )
504                 return False;   /* treat them as different on failure */
505
506         /*
507          * Save the data from gethostbyname() in "hostName1" so we can
508          * call gethostbyname() again without losing it.
509          */
510         strcpy(hostName1, host_ret->h_name);
511         if ( (tp = DtStrchr(hostName1,'.')) != NULL )
512                 *tp = '\0';
513
514         /*
515          * Try comparing again -- avoiding another gethostbyname
516          * if successful.
517          */
518         if ( !strcmp( hostName1,hostName2) )
519                 return True;
520
521         /* restore the dot if necessary */
522         if ( tp) *tp = '.';
523
524         if ( (host_ret = _XGethostbyname(hostName2, host_buf)) == NULL )
525                 return False;   /* treat them as different on failure */
526
527         if ( !strcmp(hostName1, host_ret->h_name) )
528                 return  True;   
529
530         return False;   /* The names are different */
531 }
532
533
534 /******************************************************************************
535  *
536  * _DtGetActionIconDefault - 
537  *      return the default action icon name string based on the "*ActionIcon"
538  *      X resource and the DtACTION_ICON_DEFAULT value.
539  *
540  *****************************************************************************/
541
542 char *
543 _DtGetActionIconDefault ( void )
544 {
545    static char *defaultActionIcon = NULL;
546    char         nameBuf[_DtAct_MAX_BUF_SIZE];
547    char         classBuf[_DtAct_MAX_BUF_SIZE];
548    XrmValue     resource_value;
549    XrmDatabase  db;
550    char         *rep_type;
551    int          bytesNeeded;
552    char *name;
553    char *class;
554
555    _DtSvcProcessLock();
556    if ( defaultActionIcon ) {
557         _DtSvcProcessUnlock();
558         return XtNewString(defaultActionIcon);
559       }
560
561    bytesNeeded = strlen(DtACTION_ICON_RESOURCE_NAME) 
562                        + strlen(_DtApplicationName) + 4;
563    if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
564         name = XtMalloc(bytesNeeded);
565    else
566         name = nameBuf;
567
568    sprintf (name, "%s*%s",
569         _DtActNULL_GUARD( _DtApplicationName) , DtACTION_ICON_RESOURCE_NAME);
570
571
572    bytesNeeded = strlen(DtACTION_ICON_RESOURCE_CLASS) 
573                        + strlen(_DtApplicationClass) + 4;
574    if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
575         class = XtMalloc(bytesNeeded);
576    else
577         class = classBuf;
578    sprintf (class, "%s*%s", 
579         _DtActNULL_GUARD(_DtApplicationClass) , DtACTION_ICON_RESOURCE_CLASS);
580
581    if(_DtDisplay)
582         db = XtDatabase (_DtDisplay);
583    else
584         db = 0;
585    if (db && XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
586       defaultActionIcon = (char *) XtNewString (resource_value.addr);
587    else
588       defaultActionIcon = (char *) XtNewString (DtACTION_ICON_DEFAULT);
589
590    if ( name != nameBuf )
591         XtFree(name);
592    if ( class != classBuf )
593         XtFree(class);
594
595    _DtSvcProcessUnlock();
596    return XtNewString(defaultActionIcon);
597 }
598
599
600 /******************************************************************************
601  *
602  * _DtGetExecHostsDefault - 
603  *      Returns the default execution host string based on the "*executionHosts"
604  *      X resource and the default vaule of DtEXEC_HOSTS_DEFAULT.
605  *
606  * PARAMETERS: None.
607  *
608  * RETURNS: char *
609  *
610  *****************************************************************************/
611
612 char *
613 _DtGetExecHostsDefault ( void )
614 {   
615    static char *executionHosts = NULL;
616    char         nameBuf[_DtAct_MAX_BUF_SIZE];
617    char         classBuf[_DtAct_MAX_BUF_SIZE];
618    XrmValue resource_value;
619    XrmDatabase db;
620    char *rep_type;
621    char *name, *class;
622    int bytesNeeded;
623
624    _DtSvcProcessLock();
625    if ( executionHosts ) {
626         _DtSvcProcessUnlock();
627         return XtNewString(executionHosts);
628       }
629
630    bytesNeeded = strlen(DtEXEC_HOSTS_NAME) + strlen(_DtApplicationName) + 4;
631
632    if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
633         name = XtMalloc(bytesNeeded);
634    else
635         name = nameBuf;
636    sprintf (name, "%s*%s",
637         _DtActNULL_GUARD(_DtApplicationName), DtEXEC_HOSTS_NAME);
638
639   
640    bytesNeeded = strlen(DtEXEC_HOSTS_CLASS) + strlen(_DtApplicationClass) + 4;
641    if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
642         class = XtMalloc(bytesNeeded);
643    else
644         class = classBuf;
645    sprintf (class, "%s*%s",
646         _DtActNULL_GUARD(_DtApplicationClass), DtEXEC_HOSTS_CLASS);
647
648    db = XtDatabase (_DtDisplay);
649    if (db && XrmGetResource (db, name, class, &rep_type, &resource_value))
650       executionHosts = (char *) XtNewString (resource_value.addr);
651    else
652       executionHosts = (char *) XtNewString (DtEXEC_HOSTS_DEFAULT);
653
654    if ( name != nameBuf )
655            XtFree (name);
656    if ( class != classBuf )
657            XtFree (class);
658
659    _DtSvcProcessUnlock();
660    return XtNewString(executionHosts);
661 }
662
663
664 /******************************************************************************
665  char *_DtGetDtTmpDir() 
666
667     check resource; then go to internal default for value of Dt Tmp
668     directory path.  This function returns a newly allocated string, it is
669     up to the caller to free it.
670  
671  *****************************************************************************/
672 char *_DtGetDtTmpDir(void)
673 {
674     static char *DtTmpDirPath = NULL;
675     char *dirBuf = NULL;
676     char nameBuf[_DtAct_MAX_BUF_SIZE];
677     char classBuf[_DtAct_MAX_BUF_SIZE];
678     char *name;
679     char *class;
680     int bytesNeeded;
681     char        *rep_type;
682     XrmValue    resource_value;
683     XrmDatabase db;
684
685     _DtSvcProcessLock();
686     if ( DtTmpDirPath ) {
687             _DtSvcProcessUnlock();
688             return XtNewString(DtTmpDirPath);
689           }
690
691     /*
692      * Check if a resource has been set for the tmp dir location
693      */
694  
695     bytesNeeded = strlen(DtACTION_DTTMPDIR_RESOURCE_NAME)
696                    + strlen(_DtApplicationName) + 4;
697     if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
698         name = XtMalloc(bytesNeeded);
699     else
700         name = nameBuf;
701
702     sprintf (name, "%s*%s",
703         _DtActNULL_GUARD( _DtApplicationName) , DtACTION_DTTMPDIR_RESOURCE_NAME);
704
705
706     bytesNeeded = strlen(DtACTION_DTTMPDIR_RESOURCE_CLASS)
707                        + strlen(_DtApplicationClass) + 4;
708     if ( bytesNeeded > _DtAct_MAX_BUF_SIZE )
709         class = XtMalloc(bytesNeeded);
710     else
711         class = classBuf;
712     sprintf (class, "%s*%s",
713         _DtActNULL_GUARD(_DtApplicationClass) , DtACTION_DTTMPDIR_RESOURCE_CLASS);
714
715     db = XtDatabase (_DtDisplay);
716     if (db && XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
717       DtTmpDirPath = (char *) XtNewString (resource_value.addr);
718     else
719     {
720       /* RWV: is this the right HOME if we've changed user id? */
721       char *home = getenv("HOME");
722       dirBuf = XtCalloc(1, MAXPATHLEN + 1);
723       snprintf(dirBuf, MAXPATHLEN, "%s/%s", home, DtACTION_DTTMPDIR_DEFAULT);
724       DtTmpDirPath = XtNewString(dirBuf);
725       XtFree(dirBuf);
726     }
727     
728     /*
729      * Save a copy of the path for future reference
730      */
731     _DtSvcProcessUnlock();
732     return XtNewString(DtTmpDirPath);
733 }
734
735 /*****************************************************************************
736  *
737  * _DtActGenerateTmpFile(char *dir, char *format)
738  *      Generate a temporary file  in the directory and format specified.
739  *      format is assumed to contain a single %s and to be in a form
740  *      suitable for use by sprintf().  If  "dir" is not accessable or NULL
741  *      then the default CDE tmp dir, the contents of the "TMPDIR" environment
742  *      variable, the P_tmpdir defined in stdio.h and finally "/tmp" are 
743  *      tried in turn.  
744  *              This function returns a newly malloc-ed string containing a
745  *      full path for a new tmp file.  The open file descriptor for the tmp
746  *      file is returned through an int pointer (*fd).
747  *
748  *      If no unused tmp name can be generated (within 1000 tries) then this
749  *      function returns NULL.
750  *
751  *****************************************************************************/
752
753 char *
754 _DtActGenerateTmpFile(char *dir,char *format,mode_t mode,int *fd)
755 {
756         int pid; 
757         static unsigned long nameCount = 0xA;
758         int free_d = 0;
759         int countTrys = 0;      /* count of the number of tmp names tried */
760         char nameBuf[MAXPATHLEN];
761         char *d = dir;
762         char *f = format;
763         char *base;
764       
765         struct stat statbuf;
766
767         pid = getpid();
768
769         if ( !d )
770         {
771                 free_d = 1;
772                 d = _DtGetDtTmpDir();
773         }
774         if ( !f )
775                 f = "%s";
776
777         /*
778          * Make sure the desired directory is avaliable
779          * if not try P_tmpdir (i.e. /usr/tmp), finally resort
780          * to "/tmp" if no other tmp dir can be accessed.
781          */
782         if ( stat(d,&statbuf) )
783         {
784                 /* 
785                  * The passed in directory cannot be accessed so
786                  * try some alternatives.
787                  */
788                 int i;
789                 static char *AltTmpDirs[] = {
790                                         NULL,   /* reserved for getenv() */
791                                         P_tmpdir,       /* from stdio.h */
792                                         "/tmp",
793                                         NULL,
794                                       }; 
795
796                 _DtSvcProcessLock();
797                 AltTmpDirs[0] = getenv("TMPDIR");
798
799                 if ( free_d ) 
800                 {
801                         XtFree(d);
802                         free_d = 0;
803                 }
804
805                 /*
806                  * Because "free_d" is false (i.e.0) at this point any
807                  * pointer assigned to d from the static array AltTmpDirs
808                  * is protected from being erroneously freed at the
809                  * end of this function.
810                  */
811                 myassert(free_d == 0);
812
813                 for ( i= 0; i < sizeof(AltTmpDirs)/sizeof(char *); i++ )
814                 {
815                         if ( !(d = AltTmpDirs[i]) )
816                                 continue;
817                         if ( stat(d,&statbuf) == 0 )
818                                 break;
819                 }
820                 _DtSvcProcessUnlock();
821
822                 if ( !d )
823                 {
824                         myassert(0 /* this should never happen */);
825                         return NULL;
826                 }
827
828         }
829
830         do {
831
832                 _DtSvcProcessLock();
833                 sprintf(nameBuf,"%lx_%d",nameCount++,pid);
834                 _DtSvcProcessUnlock();
835                 base = XtNewString(nameBuf);
836                 /*
837                  * Convert the base name to the desired format
838                  */
839                 sprintf(nameBuf,f,base);
840                 XtFree(base);
841
842                 /*
843                  * If the format string does not allow for variation
844                  * of the tmp file name (i.e. no %s in format) then
845                  * there is no sense trying more than once.
846                  */
847                 if ( countTrys > 0 && (strcmp(f,nameBuf) == 0))
848                     return NULL;
849
850                 base = XtNewString(nameBuf);
851
852                 /*
853                  * generate the full path name to the new tmp file
854                  */ 
855                 /* if ( d[strlen(d)-1] != '/' ) */
856                 if ( *DtPrevChar(d,d + strlen(d)) != '/' )
857                         sprintf(nameBuf,"%s/%s",d,base);
858                 else
859                         sprintf(nameBuf,"%s%s",d,base);
860                 
861                 XtFree(base);
862
863                /*
864                 * Check if such a file already exists.
865                 */
866                 *fd = open(nameBuf,(O_WRONLY | O_CREAT | O_EXCL), mode );
867         }
868         while ( *fd == -1 && errno == ENOENT && countTrys++ < 1000 );
869
870         if ( free_d )
871                 XtFree(d);
872
873         if ( *fd == -1 )
874                 return NULL;    /* unable to generate desired name format */
875
876         /* 
877          * The file has been successfully created -- return the name for 
878          * use by the caller.  The already open fd is also available to the
879          * calling function.  It is up to the caller to close the file.
880          */
881
882         return XtNewString(nameBuf);
883 }
884
885 /*******************************************************************************
886  * _DtRemoveTrailingBlanksInPlace
887  *      Removes trailing white space from the passed string.  The string
888  *      is modified in place.
889  ******************************************************************************/
890 void
891 _DtRemoveTrailingBlanksInPlace(char **s)
892 {
893         char *p;
894
895         if (!s || !strlen(*s))
896                  return;
897
898         for ( p = DtPrevChar(*s,*s + strlen(*s)); 
899               DtIsspace(p) && (p > *s); 
900               p=DtPrevChar(*s,p))
901                 *p = '\0';
902         
903 }
904
905
906 /******************************************************************************
907  *
908  * _DtExecuteAccess ( path ) 
909  *
910  * PARAMETERES:
911  *   char *path;        // path of potentially executable file
912  *
913  * For effective user id, 
914  * RETURNS: True (1) if file is executable; 
915  *          False (0)if file is not executable;
916  *          -1 if the file cannot be accessed.
917  *
918  *****************************************************************************/
919
920
921 #ifdef NGROUPS_UMAX
922 #define NGROUPS_MAX_VALUE       NGROUPS_UMAX
923 #else
924 #define NGROUPS_MAX_VALUE       NGROUPS_MAX
925 #endif
926
927 int
928 _DtExecuteAccess( const char *path )
929 {
930         int i, amode, rval;
931         uid_t euid;
932         struct stat s;
933         gid_t *pgid;
934         gid_t groupids[NGROUPS_MAX_VALUE];
935         struct group *gr;
936         _Xgetgrparams grp_buf;
937
938         if (stat( path, &s ) == -1 ) {
939                 /* could not stat file, no access */ 
940                 return -1;
941         }
942
943         euid = geteuid(); 
944
945         if( (S_IXUSR & s.st_mode) == S_IXUSR ) {
946                 if(euid == s.st_uid || euid == 0) {
947                         /* execution permitted for user */
948                         return  1;
949                 }
950         } else {
951                 if (euid == s.st_uid && euid != 0) {
952                         /* user execution not permitted */
953                         return 0;
954                 }
955         }
956
957         if( (S_IXGRP & s.st_mode) == S_IXGRP ) {
958                 if(getegid() == s.st_gid || euid == 0) {
959                         /* execution permitted for group (or superuser) */
960                         return  1;
961                 }
962
963                 i = getgroups(getgroups(0,groupids), groupids);
964
965                 if ( i > 0) {
966                         for (pgid = groupids; i--; pgid++) {
967                                 if ((gr = _XGetgrgid(*pgid, grp_buf)) != NULL)
968                                     if (gr->gr_gid == s.st_gid) {
969                                         /* execution permitted to group list */
970                                         return 1;
971                                 }
972                         }
973                 }
974         }
975  
976         if( (S_IXOTH & s.st_mode) == S_IXOTH ) {
977                 /* execution permitted for "others" */
978                 return  1;
979         }
980
981         /* no access */
982
983         return 0;
984 }
985
986 /******************************************************************************
987  *
988  * Routines to manipulate DtActionInvocationID's.
989  */
990
991 /**************************************************
992  *
993  * Allocate an unused DtActionInvocationID
994  * between 1..INT_MAX
995  */
996 DtActionInvocationID _DtActAllocID(void)
997 {
998     extern _DtActInvRecT   **_DtActInvRecArray;         /* global */
999     extern int             _actInvRecArraySize;         /* global */
1000
1001     static unsigned long   lastIdWas = 100;             /* 0..99 for errors */
1002     int                    i, found;
1003
1004
1005     _DtSvcProcessLock();
1006     do {
1007         found = 1;
1008
1009         /*
1010          * Need to track down better define than INT_MAX to determine cap
1011          */
1012         if ( lastIdWas == INT_MAX )
1013             lastIdWas = 1;
1014         else
1015             lastIdWas++;
1016
1017         /*
1018          * Verify that the ID is not being used already
1019          */
1020         for ( i = 0 ; i < _actInvRecArraySize ; i++ ) {
1021             if ( _DtActInvRecArray[i] ) {
1022                 if ( (_DtActInvRecArray[i] -> id) == lastIdWas )
1023                     found = 0;
1024             }
1025         }
1026     } while ( ! found );
1027     
1028     _DtSvcProcessUnlock();
1029     return( lastIdWas );
1030 }
1031
1032
1033 /**************************************************
1034  *
1035  * Allocate an Invocation Record
1036  */
1037 _DtActInvRecT *_DtActAllocInvRec(void)
1038 {
1039     extern _DtActInvRecT   **_DtActInvRecArray;         /* global */
1040     extern int             _actInvRecArraySize;         /* global */
1041
1042     int                    i, newslot;
1043     static int             first_time = 1;
1044
1045
1046     _DtSvcProcessLock();
1047     /*
1048      * If first time, malloc array of InvRec pointers
1049      */
1050     if (first_time) {
1051         _actInvRecArraySize = _START_INVREC_SIZE;
1052         _DtActInvRecArray = (_DtActInvRecT **) XtMalloc(sizeof(_DtActInvRecT *)
1053                                                         * _actInvRecArraySize);
1054
1055         /*
1056          * A NULL indicates an available slot.
1057          */
1058         for ( i = 0; i < _actInvRecArraySize; i++ )
1059             _DtActInvRecArray[i] = NULL;
1060
1061         first_time = 0;
1062     }
1063
1064     /*
1065      * Look through existing list of InvRec's for an available slot.
1066      */
1067     newslot = -1;
1068     for ( i = 0; i < _actInvRecArraySize; i++ ) {
1069         if ( _DtActInvRecArray[i] == NULL ) {
1070             newslot = i;
1071             break;
1072         }
1073     }
1074
1075     if ( newslot == -1 ) {
1076         /*
1077          * Need to grow InvRecArray since current one is full.
1078          */
1079         _actInvRecArraySize += 10;
1080         _DtActInvRecArray = (_DtActInvRecT **)
1081                                 XtRealloc( (char *) _DtActInvRecArray,
1082                                                 sizeof(_DtActInvRecT *)
1083                                                 * _actInvRecArraySize   );
1084
1085         /*
1086          * NULL out new entries
1087          */
1088         for ( i = _actInvRecArraySize-10; i < _actInvRecArraySize; i++ )
1089             _DtActInvRecArray[i] = NULL;
1090
1091         newslot = _actInvRecArraySize-10;
1092     }
1093
1094     /*
1095      * Hang a new InvRec off the array and initialize all to zero.
1096      */
1097     _DtActInvRecArray[newslot] = (_DtActInvRecT *)
1098                                         XtCalloc(1, sizeof(_DtActInvRecT) );
1099     _DtActInvRecArray[newslot]->id =  _DtActAllocID();
1100     _DtSvcProcessUnlock();
1101
1102     return( _DtActInvRecArray[newslot] );
1103 }
1104
1105 /******************************************************************************
1106  *
1107  * _DtActFreeChildRec()
1108  *
1109  * Completely free the contents of, and free the existence of a childRec.
1110  *
1111  *****************************************************************************/
1112
1113 static void
1114 _DtActFreeChildRec( _DtActChildRecT *childRecP)
1115 {
1116     CallbackData *data;
1117
1118
1119     XtFree((char *)childRecP->argMap);
1120
1121     if ( IS_CMD( childRecP->mask ) ) {
1122         XtFree((char *) childRecP->u.cmd.TTProcId);
1123
1124         if (childRecP->u.cmd.reqMessage) {
1125             data = (CallbackData *)
1126                 tt_message_user(childRecP->u.cmd.reqMessage,0);
1127
1128             if (data) {
1129                 XtFree((char *) data->actionLabel);
1130
1131                 /*
1132                  * tjg: question if we should do this
1133                  *
1134                  * if (data->actionPtr)
1135                  *      _DtFreeActionStruct( data->actionPtr );
1136                  */
1137
1138                 if (data->requestPtr)
1139                     _DtFreeRequest(data->requestPtr);
1140
1141                 XtFree((char *) data);
1142             }
1143
1144             tttk_message_destroy(childRecP->u.cmd.reqMessage);
1145         }
1146     }
1147     else if ( IS_TT_MSG( childRecP->mask ) ) {
1148         tt_free((char *) childRecP->u.tt.TTProcId);
1149
1150         if (childRecP->u.tt.reqMessage) {
1151             data = (CallbackData *)
1152                 tt_message_user(childRecP->u.tt.reqMessage,0);
1153
1154             if (data) {
1155                 XtFree((char *) data->actionLabel);
1156
1157                 /*
1158                  * tjg: question if we should do this
1159                  *
1160                  * if (data->actionPtr)
1161                  *      _DtFreeActionStruct( data->actionPtr );
1162                  */
1163
1164                 if (data->requestPtr)
1165                     _DtFreeRequest(data->requestPtr);
1166
1167                 XtFree((char *) data);
1168             }
1169             tttk_message_destroy(childRecP->u.tt.reqMessage);
1170         }
1171     }
1172
1173     XtFree( (char *) childRecP );
1174 }
1175
1176 /******************************************************************************
1177  *
1178  * _DtActDeleteChildRec()
1179  *
1180  * Within an existing invRec, delete one of its children.   Squeeze the
1181  * childRec array if needed (but don't bother to realloc smaller).
1182  * 
1183  *      Returns 1 for successful deletion.
1184  *      Returns 0 if unable to delete the record.
1185  *
1186  ******************************************************************************/
1187 int _DtActDeleteChildRec( _DtActInvRecT *invp, _DtActChildRecT *childp)
1188 {
1189         int i,j;
1190
1191         if ( !invp || !childp )
1192                 return 0;
1193
1194         for ( i = 0; i < invp->numChildren; i++ )
1195         {
1196                 if ( invp->childRec[i] == childp )
1197                 {
1198                         _DtActFreeChildRec(childp);
1199                         invp->numChildren--;
1200                         invp->childRec[i] = NULL;
1201                         /*
1202                          * Close the potential gap created in the array.
1203                          */
1204                         for ( j = i; j < invp->numChildren; j++)
1205                         {
1206                                 invp->childRec[i] = invp->childRec[i+1];
1207                                 invp->childRec[i+1] = NULL;
1208                         }
1209                         return 1;
1210                 }
1211         } 
1212         /* child not found */
1213         return 0;
1214 }
1215
1216 /******************************************************************************
1217  *
1218  * _DtActDeleteInvRec( id )
1219  * Delete an Action Invocation Record given an invocation id
1220  *      Returns 1 for successful deletion.
1221  *      Returns 0 if unable to delete the record.
1222  *      Returns -1 if the record is not found.
1223  *
1224  * note: as pointers are free'd, they should be set to NULL
1225  *
1226  ******************************************************************************/
1227 int _DtActDeleteInvRec( DtActionInvocationID id )
1228 {
1229     int i;
1230     CallbackData *data;
1231
1232     _DtSvcProcessLock();
1233     for ( i = 0; i < _actInvRecArraySize; i++ ) 
1234     {
1235         if ( _DtActInvRecArray[i] ) 
1236         {
1237             if ( (_DtActInvRecArray[i]->id) == id ) 
1238             {
1239                 _DtActInvRecT *invp  = _DtActInvRecArray[i];
1240                 _DtActArgInfo *infop;
1241                 int j;
1242                
1243                 /*
1244                  * Check for any tmp files created to house buffers
1245                  * Delete them if they still exist.
1246                  */
1247
1248                 for ( j = 0; j < invp->ac; j++)
1249                 {
1250                     infop = &invp->info[j];
1251
1252                     if (IS_BUFFER_OBJ(infop->mask) && IS_FILE_OBJ(infop->mask))
1253                     {
1254                         myassert((infop->name != NULL) && (*infop->name != '\0'));
1255                         if ( !infop->name )
1256                             continue;
1257                         /*
1258                          * These tmp names should have been created by
1259                          * the execution management code and such names
1260                          * should not refer to directories. We will not
1261                          * check that assertion here.
1262                          */
1263                         if ( !IS_WRITE_OBJ(infop->mask) )
1264                         {
1265                              mode_t mode = ( S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP);
1266                              /*
1267                               * Change the file's mode before deleting
1268                               */
1269                              chmod(infop->name,mode);
1270                         }
1271                         (void)unlink(infop->name);
1272                         RESET_FILE_OBJ(infop->mask);
1273                     } 
1274  
1275                 }
1276
1277                 /*
1278                  * Cleanup invocation record.  Don't worry about squeezing
1279                  * and realloc'ing the _DtActInvRecArray.
1280                  */
1281                 _DtActFreeInvRec( _DtActInvRecArray[i] );
1282                 _DtActInvRecArray[i] = NULL;
1283                 _DtSvcProcessUnlock();
1284                 return 1; /* successfully deleted */
1285             }
1286         }
1287     }
1288
1289     _DtSvcProcessUnlock();
1290     return -1;  /* not found */ 
1291 }
1292
1293 /**************************************************
1294  *
1295  * Allocate a Child Record; hang off parent
1296  */
1297 _DtActChildRecT *_DtActAllocChildRec( _DtActInvRecT *invRec )
1298 {
1299     _DtActChildRecT        *tchildRec;
1300
1301
1302     if ( invRec ) {
1303         (invRec->numChildren)++;
1304         if ( invRec->numChildren == 1 ) {
1305             /*
1306              * First child.  Build array.
1307              */
1308             invRec->childRec = (_DtActChildRecT **)
1309                                         XtMalloc( sizeof(_DtActChildRecT *) );
1310         }
1311         else {
1312             invRec->childRec = (_DtActChildRecT **)
1313                                         XtRealloc( (char *) invRec->childRec,
1314                                             sizeof(_DtActChildRecT *) *
1315                                             invRec->numChildren);
1316         }
1317
1318         /*
1319          * Hang a child rec off and initialize
1320          */
1321         invRec->childRec[invRec->numChildren - 1] = (_DtActChildRecT *)
1322                                 XtMalloc( sizeof(_DtActChildRecT) );
1323
1324         tchildRec = invRec->childRec[invRec->numChildren - 1];  /* shorthand */
1325
1326         memset(tchildRec, 0, sizeof(_DtActChildRecT));
1327         tchildRec->childId     = invRec->numChildren;   /* serial # of sorts */
1328
1329         /* tchildRec->u.*      = initialized elsewhere */
1330
1331         return( (_DtActChildRecT *) tchildRec );
1332     }
1333     else {
1334         return( (_DtActChildRecT *) NULL );     /* should not happen */
1335     }
1336 }
1337
1338
1339 /**************************************************
1340  *
1341  * Given an ID, find a DtActInvRec.
1342  */
1343 _DtActInvRecT *_DtActFindInvRec( DtActionInvocationID id )
1344 {
1345     extern _DtActInvRecT   **_DtActInvRecArray;
1346     extern int             _actInvRecArraySize;
1347
1348     int i;
1349
1350     _DtSvcProcessLock();
1351     for ( i = 0; i < _actInvRecArraySize; i++ ) {
1352         if ( _DtActInvRecArray[i] ) {
1353             if ( (_DtActInvRecArray[i]->id) == id ) {
1354                 _DtSvcProcessUnlock();
1355                 return( _DtActInvRecArray[i] );         /* match */
1356             }
1357         }
1358     }
1359
1360     _DtSvcProcessUnlock();
1361     return( (_DtActInvRecT *) NULL );                   /* no match */
1362 }
1363
1364 /**************************************************
1365  *
1366  * Given an ID and a childId, find a DtActChildRec.
1367  */
1368 _DtActChildRecT *_DtActFindChildRec( 
1369         DtActionInvocationID id,
1370         unsigned long        childId)
1371 {
1372     int           i;
1373     _DtActInvRecT *invRec;
1374
1375
1376     if ( (invRec = _DtActFindInvRec( id )) == NULL )
1377         return( (_DtActChildRecT *) NULL );             /* no match */
1378
1379     for ( i = 0; i < invRec->numChildren; i++ ) {
1380         if ( invRec->childRec[i] ) {
1381             if ( (invRec->childRec[i]->childId) == childId ) {
1382                 return( invRec->childRec[i] );          /* match */
1383             }
1384         }
1385     }
1386
1387     return( (_DtActChildRecT *) NULL );                 /* no match */
1388           
1389 }
1390
1391 /******************************************************************************
1392  *
1393  * _DtActFreeInvRec( p )
1394  *      Free a given action invocation record.
1395  *
1396  *****************************************************************************/
1397
1398 static void
1399 _DtActFreeInvRec( _DtActInvRecT *invp )
1400 {
1401     int i;
1402
1403
1404     /*
1405      *  Free info argMap of action arguments.
1406      */
1407     if ( invp->info ) {
1408          for ( i=0; i < invp->ac; i++ ) {
1409              XtFree(invp->info[i].type);
1410              XtFree(invp->info[i].name);
1411          }    
1412          XtFree((char *)invp->info);
1413          invp->info = NULL;
1414     }
1415     
1416
1417     /*
1418      * Free up cached data if any.   This is a rude move since it should
1419      * have been expected to be uploaded by now.
1420      */
1421     if (invp->cachedUploadCnt) {
1422         for ( i = 0; i < invp->cachedUploadCnt; i++ ) {
1423             _DtActFreeArgArray( invp->cachedUploads[i].newArgp,
1424                                 invp->cachedUploads[i].newArgc);
1425         }
1426         XtFree( (char *) invp->cachedUploads );
1427     }
1428
1429     /*
1430      * Free up child records if necessary.
1431      */
1432     for ( i = 0; i < invp->numChildren; i++ ) {
1433         /*
1434          * Remove each child
1435          */
1436         _DtActFreeChildRec( invp->childRec[i] );
1437     }
1438     XtFree( (char *) invp->childRec );
1439
1440     XtFree((char *)invp);
1441 }
1442
1443 /******************************************************************************
1444  *
1445  * Add an update to the invocation upload cache.
1446  */
1447 static void _DtActCacheArgs(
1448     _DtActInvRecT   *invRecP,
1449     DtActionArg     *newArgp,
1450     int             newArgc,
1451     DtActionStatus  userStatus )
1452 {
1453
1454     /*
1455      * Grow the cache.  We'll add to it one by one, but when a cache
1456      * flush happens later, all entries will go and be freed.
1457      */
1458     if ( invRecP->cachedUploadCnt )
1459         invRecP->cachedUploads = (_DtActUpdateCache *)
1460                                  XtRealloc( (char *) invRecP->cachedUploads,
1461                                             (invRecP->cachedUploadCnt+1) *
1462                                             sizeof(_DtActUpdateCache) );
1463     else
1464         invRecP->cachedUploads = (_DtActUpdateCache *)
1465                                  XtMalloc( sizeof(_DtActUpdateCache) );
1466
1467     invRecP->cachedUploads[invRecP->cachedUploadCnt].newArgp = newArgp;
1468     invRecP->cachedUploads[invRecP->cachedUploadCnt].newArgc = newArgc;
1469     invRecP->cachedUploads[invRecP->cachedUploadCnt].userStatus = userStatus;
1470
1471     (invRecP->cachedUploadCnt)++;
1472 }
1473
1474 /******************************************************************************
1475  *
1476  * Routine to evaluate the done-ness of an invocation session.
1477  *
1478  * This is the key evalulation routine that should be used at all
1479  * execution leaf nodes to decide if and how an "invocation session"
1480  * should be shutdown.
1481  *
1482  *****************************************************************************/
1483 unsigned long _DtActEvalChildren(DtActionInvocationID id)
1484 {
1485         _DtActInvRecT *invRec;
1486         unsigned long cstats;
1487         int i;
1488
1489
1490         invRec = _DtActFindInvRec( id );
1491         myassert(invRec);
1492
1493         if ( IS_INV_FINISHED(invRec->state) ) { 
1494             /*
1495              * All children have been launched (FINISHED), or we're done
1496              * launching new children (CANCEL), so now look at the status
1497              * of each child to figure out what to do.
1498              */
1499
1500             if ( invRec->numChildren ) {
1501                 cstats = 0;
1502                 for ( i = 0; i < invRec->numChildren; i++ ) {
1503                     cstats |= invRec->childRec[i]->childState;
1504                 }
1505             }
1506             else {
1507                 /*
1508                  * The invocation is finished, and yet there are no
1509                  * children.  Probably the result of a cancel, so
1510                  * cobble up a child status of _DtActCHILD_CANCELED.
1511                  */
1512                 cstats = _DtActCHILD_CANCELED;
1513             }
1514
1515             /*
1516              * Return worst case information on child.
1517              */
1518             if ( cstats & _DtActCHILD_UNKNOWN )
1519                                         return( _DtActCHILD_UNKNOWN );
1520             else if ( IS_CHILD_PENDING_START(cstats) )
1521                                         return ( _DtActCHILD_PENDING_START );
1522             else if ( IS_CHILD_ALIVE_UNKOWN( cstats ) )
1523                                         return ( _DtActCHILD_ALIVE_UNKNOWN );
1524             else if ( IS_CHILD_ALIVE( cstats ) )
1525                                         return ( _DtActCHILD_ALIVE );
1526             else if ( IS_CHILD_FAILED( cstats ) )
1527                                         return ( _DtActCHILD_FAILED );
1528             else if ( IS_CHILD_CANCELED( cstats ) )
1529                                         return ( _DtActCHILD_CANCELED );
1530             else if ( IS_CHILD_DONE( cstats ) )
1531                                         return ( _DtActCHILD_DONE );
1532             else
1533                                         return( _DtActCHILD_UNKNOWN );
1534         }
1535         else {
1536             /*
1537              * Not all the children have been launched, so no need to do
1538              * an analysis.
1539              */
1540             return( _DtActCHILD_UNKNOWN );
1541         }
1542 }
1543
1544 /******************************************************************************
1545  *
1546  * _DtActExecutionLeafNodeCleanup()
1547  *
1548  * At natural execution path leafs within the DtAction code,
1549  * _DtActExecutionLeafNodeCleanup() should be called.
1550  *
1551  * See rev 1.19 to see how DtActionQuit() functionality was
1552  * once integrated into this routine.
1553  *
1554  * Basic shutdown process:
1555  *
1556  *    1. If not in a DtACTION_DONE* state and there are no arguments,
1557  *       do nothing.
1558  *
1559  *    2. If not in a DtACTION_DONE* state and there are arguments,
1560  *       generate a DtACTION_STATUS_UPDATE with arguments.
1561  *
1562  *    3. If in a DtACTION_DONE* state with or without arguments,
1563  *       generate a DtACTION_DONE* with or without arguments,
1564  *       and do a total shutdown of the entire invocation session.
1565  */
1566 void _DtActExecutionLeafNodeCleanup(
1567     DtActionInvocationID  id,
1568     DtActionArg           *newArgp,
1569     int                   newArgc,
1570     int                   respectQuitBlock )
1571 {
1572     unsigned long  evalStatus;
1573     DtActionStatus userStatus;
1574     _DtActInvRecT *invRecP;
1575     int            flushCache, useCache;
1576     int            i;
1577
1578     flushCache = 0;     /* flush the cache 1st */
1579     useCache   = 0;     /* must cache */
1580
1581     invRecP = _DtActFindInvRec( id );
1582     if ( !invRecP ) {
1583         /*
1584          * The whole invocation session is already down.
1585          *
1586          * One possible way that this can happen is a fast
1587          * tt_reply that triggers a DtACTION_DONE *before*
1588          * the _DtActTimerCB() routine can go off.
1589          *
1590          * Free any args since they won't be going anywhere.
1591          */
1592         _DtActFreeArgArray( newArgp, newArgc );
1593         return;
1594     }
1595
1596     if ( !IS_INV_ID_RETURNED(invRecP->state) )
1597         useCache = 1;           /* Need to cache */
1598     else if (invRecP->cachedUploadCnt)
1599         flushCache = 1;         /* Can do uploads 1st, if any */
1600
1601     /*
1602      * See if we still need to generate the DtACTION_INVOKED
1603      * update.
1604      */
1605     if (IS_INV_FINISHED(invRecP->state)) {
1606         if ( IS_INV_INDICATOR_ON(invRecP->state) )
1607         {
1608             /* Turn off the activity indicator */
1609             _DtSendActivityDoneNotification();
1610             RESET_INV_INDICATOR_ON(invRecP->state);
1611         }
1612         if ( CALL_INV_CB(invRecP->state) ) {
1613             if ( invRecP->cb  ) {
1614                 (invRecP->cb)( id, invRecP->client_data,
1615                     (DtActionArg *) NULL, 0,
1616                     DtACTION_INVOKED );
1617             }
1618             SET_INV_CB_CALLED(invRecP->state);
1619         }
1620     }
1621
1622     /*
1623      * See if we have any cache stuff to flush
1624      */
1625     if (flushCache) {
1626         for ( i = 0; i < invRecP->cachedUploadCnt; i++ ) {
1627             if (invRecP->cb) {
1628                 (invRecP->cb)( id, invRecP->client_data,
1629                                 invRecP->cachedUploads[i].newArgp,
1630                                 invRecP->cachedUploads[i].newArgc,
1631                                 invRecP->cachedUploads[i].userStatus );
1632             }
1633             else {
1634                 /*
1635                  * We normally won't have cached data if no cb.
1636                  */
1637                 _DtActFreeArgArray( invRecP->cachedUploads[i].newArgp,
1638                                     invRecP->cachedUploads[i].newArgc);
1639             }
1640         }
1641
1642         XtFree( (char *) invRecP->cachedUploads );
1643         invRecP->cachedUploadCnt = 0;
1644     }
1645
1646     /*
1647      * Data compression
1648      *
1649      * If 'newArgp' is nothing but DtACTION_NULLARGS, free it and the
1650      * following code will respond correctly.
1651      *
1652      * If the user did not register a callback, then free 'newArgp'
1653      * since it won't be going anywhere.
1654      */
1655     if ( !(invRecP->cb) ) {
1656         _DtActFreeArgArray( newArgp, newArgc );
1657         newArgc = 0;
1658     }
1659     else {
1660         if (newArgp) {
1661             for ( i = 0; i < newArgc; i++ ) {
1662                 if ( newArgp[i].argClass != DtACTION_NULLARG ) {
1663                     break;
1664                 }
1665             }
1666             if ( i == newArgc ) {
1667                 _DtActFreeArgArray( newArgp, newArgc );
1668                 newArgc = 0;
1669                 newArgp = NULL;
1670             }
1671         }
1672     }
1673         
1674     /*
1675      * See what the overall invocation session status is given the
1676      * child's possible status change.
1677      */
1678     evalStatus = _DtActEvalChildren(id);
1679     switch (evalStatus) {
1680         case _DtActCHILD_DONE:
1681             userStatus = DtACTION_DONE;
1682             break;
1683         case _DtActCHILD_CANCELED:
1684             userStatus = DtACTION_CANCELED;
1685             break;
1686         case _DtActCHILD_FAILED:
1687             userStatus = DtACTION_FAILED;
1688             break;
1689         default:
1690             /*
1691              * This is pseudo correct.   The setting here just causes us
1692              * to dive into other test conditions.
1693              */
1694             userStatus = DtACTION_STATUS_UPDATE;
1695             break;
1696     }
1697
1698     /****************************************************************
1699      *
1700      * Even though the invocation session appears "done", the
1701      * following test case may convert us into doing just
1702      * an "update".
1703      */
1704     if ( (useCache) && (userStatus != DtACTION_STATUS_UPDATE) ) {
1705         /*
1706          * The session is done, but we're in a caching situation,
1707          * so convert this "done" status to an "update" status.
1708          * When we get out of the caching situation, another evaluation
1709          * (by way of a XtTimer) will re-generate the "done" status
1710          * for us.
1711          */
1712         userStatus = DtACTION_STATUS_UPDATE;
1713     }
1714
1715     if ( userStatus == DtACTION_STATUS_UPDATE ) {
1716         /*
1717          * Some sort of DtACTION_STATUS_UPDATE state.
1718          *
1719          * Suppress giving an update if there are no arguments - it's
1720          * a waste of effort.
1721          */
1722         if ( (newArgc) && (invRecP->cb) ) {
1723             if (useCache) {
1724                 _DtActCacheArgs( invRecP, newArgp, newArgc, userStatus );
1725             }
1726             else {
1727                 (invRecP->cb)(  id, invRecP->client_data,
1728                                 newArgp, newArgc,
1729                                 userStatus );
1730             }
1731         }
1732     }
1733     else {
1734         /*
1735          * Some sort of DtACTION_DONE* state
1736          *
1737          * We are not under a caching condition, so we must handle the
1738          * shutdown here.  No matter whether we have new arguments, we
1739          * need to deliver the DtACTION_DONE* status.
1740          */
1741         if ( invRecP->cb ) {
1742             if (useCache) {
1743                 _DtActCacheArgs( invRecP, newArgp, newArgc, userStatus );
1744             }
1745             else {
1746                 (invRecP->cb)( id, invRecP->client_data,
1747                                    newArgp, newArgc,
1748                                    userStatus );
1749             }
1750         }
1751
1752         /*
1753          * Delete the entire invocation session.  "The Final Act"!
1754          */
1755         _DtActDeleteInvRec( id );
1756     }
1757 }
1758
1759 /******************************************************************************
1760  *
1761  * Create an array of N DtACTION_NULLARG's.
1762  */
1763 DtActionArg *_DtActMallocEmptyArgArray(int ac)
1764 {
1765     DtActionArg *newArgP;
1766     int i;
1767
1768     if ( ac == 0 )
1769         return NULL;
1770
1771     newArgP = (DtActionArg *) XtCalloc( ac, sizeof(DtActionArg) );
1772
1773     for ( i = 0; i < ac; i++ ) {
1774         newArgP[i].argClass = DtACTION_NULLARG;
1775     }
1776
1777     return(newArgP);
1778 }
1779
1780 /******************************************************************************
1781  *
1782  * Free an array of N DtActionArg's.
1783  */
1784 void _DtActFreeArgArray( DtActionArg *argp, int ac )
1785 {
1786     int i;
1787
1788     if (argp) {
1789         for ( i = 0; i < ac; i++ ) {
1790             if ( argp[i].argClass == DtACTION_FILE ) {
1791                 XtFree( argp[i].u.file.name );
1792             }
1793             else if ( argp[i].argClass == DtACTION_BUFFER ) {
1794                 XtFree( (char *) argp[i].u.buffer.bp );
1795                 XtFree( argp[i].u.buffer.type );
1796                 XtFree( argp[i].u.buffer.name );
1797             }
1798         }
1799         XtFree( (char *) argp );
1800     }
1801 }
1802
1803 /*****************************************************************************
1804  *
1805  * _DtActReadTmpFileToBuffer( fname, sizep )
1806  *
1807  *      Read the contents of the tmp file named: "fname" into a buffer.
1808  *      Return a pointer to the buffer and fill in "sizep" with the number
1809  *      of bytes consumed by the buffer.
1810  *
1811  *      In case of error return a NULL pointer and zero size.
1812  *
1813  *****************************************************************************/
1814
1815 void *
1816 _DtActReadTmpFileToBuffer( char *fname, int *sizep )
1817 {
1818    /*
1819     * Read in the current contents of the temp file into a
1820     * buffer for return to original caller.
1821     */
1822     int fd;
1823     int bytes;
1824     int size;
1825     int space;
1826     char *buf;
1827
1828     if ( !fname )
1829     {
1830         /* should never get here */
1831         myassert(fname != NULL);
1832         *sizep = 0;
1833         return NULL;
1834     }
1835
1836     if ( (fd = open(fname,O_RDONLY)) < 0 )
1837     {
1838         /* couldn't read tmp file */
1839         /* myassert( fd >= 0 ); */
1840         *sizep = 0;
1841         return NULL;
1842     }
1843
1844     buf = (char *) XtMalloc(MAX_BUF_SIZE);
1845     for (size=0, space=MAX_BUF_SIZE;
1846          (bytes=read(fd,buf+size,space)) != 0;
1847          size += bytes  )
1848     {
1849         if ( bytes < 0 )
1850         {
1851             myassert(0 /* read error on tmp file */);
1852             break;  /* return as much as we got */
1853         }
1854         if ( (space -= bytes) <= 0 )
1855         {
1856             /*
1857              * Time to allocate more space
1858              */
1859             buf= (char *)XtRealloc((char *)buf,size + bytes + MAX_BUF_SIZE);
1860             space = MAX_BUF_SIZE;
1861         }
1862         
1863     }
1864     
1865     close(fd);
1866     *sizep = size;
1867     return XtRealloc((char *)buf,size);
1868 }
1869
1870 /*****************************************************************************
1871  *
1872  * _DtActRetCmdChildArgs(childp, aargv)
1873  *
1874  *      Create an argument vector containing the returnable arguments for
1875  *      a command action child.  The vector pointer is passed in "aargv"
1876  *      space for it should already have been allocated by the caller.
1877  *      The number of returned arguments is returned by the function.
1878  *
1879  *****************************************************************************/
1880
1881 int
1882 _DtActGetCmdReturnArgs( 
1883     DtActionInvocationID invId,
1884     _DtActChildRecT *childp,
1885     DtActionArg     **aargv )
1886 {
1887     int i;
1888     _DtActInvRecT       *invp;
1889     DtActionArg         *newArgp;
1890
1891     if ( !(invp = _DtActFindInvRec(invId)) )
1892     {
1893         myassert( 0 /* this should never happen */ );
1894         *aargv = NULL;
1895         return 0;
1896     }
1897
1898     /*
1899      * Allocate enough arguments args for the original invocation
1900      */
1901
1902     *aargv = newArgp =  _DtActMallocEmptyArgArray( invp->ac );
1903
1904     for ( i = 0; i < childp->numObjects; i++ )
1905     {
1906         _DtActArgInfo   *infop;
1907         int Idx;
1908
1909         Idx = childp->argMap[i].argIdx;
1910         infop = &invp->info[Idx];
1911     
1912         if ( !IS_WRITE_OBJ(infop->mask) )
1913             continue;   /* only return writable objectes */
1914
1915         if ( IS_BUFFER_OBJ( infop->mask) )
1916         {
1917             if ( IS_FILE_OBJ( infop->mask) )
1918             {
1919                newArgp[Idx].argClass = DtACTION_BUFFER;
1920                newArgp[Idx].u.buffer.type = XtNewString(infop->type);
1921                /* 
1922                 * RWV: 
1923                 *     If did the following, the returned buffer name
1924                 *     may NOT be the same as the name which was passed
1925                 *     in to us (e.g. we were unable to create a tmp file
1926                 *     with the desired name).  At present there is nothing
1927                 *     that preseves the original buffer name.
1928                 *
1929                 *   newArgp[Idx].u.buffer.name = _DtBasename(infop->name);
1930                 *
1931                 *     instead we'll set the returned name field to NULL; this
1932                 *     is after all a new copy of the buffer.
1933                 */
1934                newArgp[Idx].u.buffer.name = NULL;
1935                newArgp[Idx].u.buffer.writable = True;
1936                newArgp[Idx].u.buffer.bp = 
1937                    _DtActReadTmpFileToBuffer( infop->name,
1938                        &newArgp[Idx].u.buffer.size );
1939             }
1940             else
1941             {
1942                 /* 
1943                  * We already have the buffer in memory
1944                  *  -- THIS SHOULD NEVER BE THE CASE FOR COMMAND ACTIONS--
1945                  */
1946                 myassert( 0 /* should never get here */ );
1947             }
1948
1949         }
1950         else if ( IS_FILE_OBJ( infop->mask) )
1951         {
1952             /*
1953              * RWV
1954              * What about file objects passed in with the String qualifier?
1955              * This returns the original string string which may or may not
1956              * have been a file name.  Are we OK here?
1957              */
1958             newArgp[Idx].argClass = DtACTION_FILE;
1959             newArgp[Idx].u.file.name = XtNewString( infop->name );
1960         } else
1961             myassert( 0 /* unsupported object */ );
1962     }
1963     
1964     return invp->ac;
1965 }
1966
1967 /*
1968  * Check the command invoker's queued commands for an instance of
1969  * the given invocation id.
1970  */
1971
1972 int
1973 _DtCmdCheckQForId(DtActionInvocationID id)
1974 {
1975         extern Cmd_RequestQueue * _DtCmdGetQueueHead( void );
1976         Cmd_RequestQueue *pNode;
1977         CallbackData *dp;
1978
1979         for ( pNode = _DtCmdGetQueueHead();
1980               pNode; pNode = pNode->next )
1981         {
1982                 dp = (CallbackData *)(pNode->success_data);
1983                 if ( dp->requestPtr->invocId == id )
1984                         return 1;       /* found a match */
1985         }
1986
1987         /* Unable to find matching invocation id in the queue */
1988         return 0;
1989 }