Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtHelp / HelpUtil.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 librararies 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: HelpUtil.c /main/19 1998/04/09 17:43:30 mgreess $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  **
27  **   File:        HelpUtil.c
28  **
29  **   Project:     Rivers Project
30  **
31  **   Description: 
32  ** 
33  **
34  **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
35  **
36  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
37  **  (c) Copyright 1993, 1994 International Business Machines Corp.
38  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
39  **  (c) Copyright 1993, 1994 Novell, Inc.
40  **
41  ****************************************************************************
42  ************************************<+>*************************************/
43
44
45 #include <sys/param.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>  /* R_OK */
50 #include <sys/stat.h>
51
52 #include <sys/types.h>
53 #define X_INCLUDE_PWD_H
54 #define XOS_USE_XT_LOCKING
55 #include <X11/Xos_r.h>
56
57 #include <Xm/Xm.h>
58 #include <Xm/XmP.h>
59 #include <Xm/MwmUtil.h>
60
61 #include <Xm/MessageB.h>
62 #include <X11/keysymdef.h>
63 #include <X11/Shell.h>
64 #include <X11/Intrinsic.h>
65 #include <X11/cursorfont.h>
66
67 /* private includes */
68 #include "Access.h"
69 #include "bufioI.h"
70 #include "DisplayAreaI.h"
71
72 #include <Dt/Help.h>
73 #include "HelpI.h"
74 #include "HelpP.h"
75 #include "StringFuncsI.h"
76 #include "HelpDialogI.h"
77 #include "HelpDialogP.h"
78 #include "HelpUtilI.h"
79 #include "HelposI.h"
80 #include "MessagesP.h"
81 #include "HelpQuickD.h"
82 #include "SetListI.h"
83 #include "DestroyI.h"
84 #include "HelpAccessI.h"
85 #include "FileUtilsI.h"
86 #include "HourGlassI.h"
87 #include "Lock.h"
88
89 #include <Dt/DtNlUtils.h>
90
91 /******* global variables *******/
92 char _DtHelpDefaultHelp4HelpVolume[] = "Help4Help";
93 char _DtHelpDefaultLocationId[] = "_HOMETOPIC";
94
95 /**** Help Util Error message Defines ****/
96
97 #define UtilMessage0    _DtHelpMsg_0010
98 #define UtilMessage2    _DtHelpMsg_0011
99
100
101 static void _DtMessageClose(
102     Widget w,
103     XtPointer client_data,
104     XEvent *event);
105 static void  CloseDefBoxCB(
106     Widget w,
107     XtPointer client_data,
108     XtPointer call_data);
109
110 /* Macro for finding a point within a gadget.
111  * Its used for item help 
112  */
113 #define  PT_IN_CHILD(X, Y, CHILD) \
114          ((((int)(X)) >= ((int) (CHILD)->core.x)) && \
115           (((int)(X)) <= ((int)((CHILD)->core.x + (CHILD)->core.width))) && \
116           (((int)(Y)) >= ((int) (CHILD)->core.y)) && \
117           (((int)(Y)) <= ((int)((CHILD)->core.y + (CHILD)->core.height))))
118
119
120 /******** useful constants ********/
121 #define EOS  '\0'
122 #define DIR_SLASH '/'
123
124 #define HUSET 8     /* message catalog set */
125
126 /******** static variables ********/
127 static char DirSlash[] = "/";
128
129 /******** data structures ********/
130 typedef struct ExecContext 
131 {
132    char * command;
133    XtPointer pDisplayArea;
134 } ExecContext;
135
136 /******** The onitem cursor (32x32, xbm format) ********/
137 #define onitem32_width 32
138 #define onitem32_height 32
139 #define onitem32_x_hot 0
140 #define onitem32_y_hot 0
141 static unsigned char onitem32_bits[] = {
142    0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x1f, 0xfc, 0xf9, 0xff, 0xe7, 0xf3,
143    0xf1, 0xff, 0xfb, 0xef, 0xe1, 0xff, 0xfd, 0xdf, 0xc1, 0xff, 0xfd, 0xdf,
144    0x83, 0xff, 0xfe, 0xbf, 0x03, 0xff, 0x7e, 0x7e, 0x03, 0xfe, 0xbe, 0x7d,
145    0x03, 0xfc, 0xbe, 0x7d, 0x03, 0xf0, 0xc1, 0x7d, 0x03, 0xe0, 0xff, 0x7e,
146    0x07, 0xc0, 0x7f, 0xbf, 0x07, 0x80, 0xbf, 0xbf, 0x07, 0x00, 0xde, 0xdf,
147    0x07, 0x00, 0xdc, 0xef, 0x07, 0x00, 0xdf, 0xf7, 0x07, 0x80, 0xdf, 0xfb,
148    0x0f, 0xc0, 0xdf, 0xfb, 0x0f, 0xc0, 0xdf, 0xfb, 0x0f, 0x81, 0xdf, 0xfb,
149    0xcf, 0x83, 0x3f, 0xfc, 0xef, 0x07, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff,
150    0xff, 0x0f, 0x3e, 0xfc, 0xff, 0x0f, 0xde, 0xfb, 0xff, 0x1f, 0xdc, 0xfb,
151    0xff, 0x1f, 0xdc, 0xfb, 0xff, 0x3f, 0xd8, 0xfb, 0xff, 0x3f, 0x3c, 0xfc,
152    0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
153
154 #define onitem32_m_width 32
155 #define onitem32_m_height 32
156 #define onitem32_m_x_hot 0
157 #define onitem32_m_y_hot 0
158 static unsigned char onitem32_m_bits[] = {
159    0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0xe0, 0x03, 0x0f, 0x00, 0xf8, 0x0f,
160    0x1f, 0x00, 0xfc, 0x1f, 0x3f, 0x00, 0xfe, 0x3f, 0x7f, 0x00, 0xfe, 0x3f,
161    0xfe, 0x00, 0xff, 0x7f, 0xfe, 0x01, 0xff, 0xff, 0xfe, 0x03, 0x7f, 0xfe,
162    0xfe, 0x0f, 0x7f, 0xfe, 0xfe, 0x1f, 0x3e, 0xfe, 0xfe, 0x3f, 0x00, 0xff,
163    0xfc, 0x7f, 0x80, 0x7f, 0xfc, 0xff, 0xc1, 0x7f, 0xfc, 0xff, 0xe3, 0x3f,
164    0xfc, 0xff, 0xe7, 0x1f, 0xfc, 0xff, 0xe3, 0x0f, 0xfc, 0xff, 0xe0, 0x07,
165    0xf8, 0x7f, 0xe0, 0x07, 0xf8, 0x7f, 0xe0, 0x07, 0xf8, 0xff, 0xe0, 0x07,
166    0xf8, 0xfe, 0xc0, 0x03, 0x38, 0xfc, 0x01, 0x00, 0x18, 0xfc, 0x01, 0x00,
167    0x00, 0xf8, 0xc3, 0x03, 0x00, 0xf8, 0xe3, 0x07, 0x00, 0xf0, 0xe7, 0x07,
168    0x00, 0xf0, 0xe7, 0x07, 0x00, 0xe0, 0xef, 0x07, 0x00, 0xe0, 0xc7, 0x03,
169    0x00, 0xc0, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00};
170
171 #if 0 /* XPM format */
172 static char * onitem32_xpm[] = {
173 /* width height ncolors cpp [x_hot y_hot] */
174 "32 32 3 1 0 0",
175 /* colors */
176 "       s iconColor1    m black c black",
177 ".      s background    m black c #969696969696",
178 "X      s iconColor2    m white c white",
179 /* pixels */
180 "  ..............................",
181 " X ..................XXXXX......",
182 " XX ...............XX     XX....",
183 " XXX .............X         X...",
184 " XXXX ...........X           X..",
185 " XXXXX ..........X           X..",
186 ". XXXXX ........X             X.",
187 ". XXXXXX .......X      XX      X",
188 ". XXXXXXX ......X     X..X     X",
189 ". XXXXXXXX  ....X     X..X     X",
190 ". XXXXXXXXXX ....XXXXX...X     X",
191 ". XXXXXXXXXXX ..........X      X",
192 ".. XXXXXXXXXXX ........X      X.",
193 ".. XXXXXXXXXXXX  .....X       X.",
194 ".. XXXXXXXXXXXXXX ...X       X..",
195 ".. XXXXXXXXXXXXXXX ..X      X...",
196 ".. XXXXXXXXXXXXX  ...X     X....",
197 ".. XXXXXXXXXXXX .....X    X.....",
198 "... XXXXXXXXXX ......X    X.....",
199 "... XXXXXXXXXX ......X    X.....",
200 "... XXXX XXXXXX .....X    X.....",
201 "... XX  . XXXXX ......XXXX......",
202 "... X .... XXXXX ...............",
203 "...  ..... XXXXX ...............",
204 "........... XXXXX ....XXXX......",
205 "........... XXXXX ...X    X.....",
206 "............ XXXXX ..X    X.....",
207 "............ XXXXX ..X    X.....",
208 "............. XXXXX .X    X.....",
209 "............. XXXX ...XXXX......",
210 ".............. X  ..............",
211 "............... ................"};
212 #endif
213
214 #define ROOT_USER 0
215 #define BIN_USER  2
216 #define SYS_USER  3
217
218 #define NO_CONDITION           0
219 #define MISMATCHING_HOME_DIRS  1
220
221 /* ------------------------------------------------------------ *
222 **
223 **  Function     trusted
224 **
225 **  Purpose      Determines if the passed help volume is a 
226 **               "trusted"  help volume or not.  We call it 
227 **               trusted if it meets the following conditions:
228 **                  1. File Owner is root, bin, or system. 
229 **                  2. File is NOT writable by group or others.
230 **                     
231 **  Returns
232 **               True   -  if the help volume IS Trusted
233 **              False   -  if the help volume is NOT Trusted
234 **
235 ** ------------------------------------------------------------ */
236 static Boolean trusted (char *hv_path)  /* Full path to the help volume */
237 {
238    struct stat buf;
239    Boolean     writable;
240
241    if ( (stat (hv_path, &buf)) == -1)
242       return False;
243
244
245 /* S_IWGRP */   /* write group */
246 /* S_IWOTH */   /* write other */
247
248   /** ---------------------------------------------------------------------- *
249    **  The help volume MUST be owned by root, bin, or sys to be trusted.
250    ** ---------------------------------------------------------------------- */
251
252    if ( buf.st_uid != ROOT_USER &&
253         buf.st_uid != BIN_USER  &&
254         buf.st_uid != SYS_USER)
255    {
256       return False;
257    }
258
259   /** ----------------------------------------------------------------------- *
260    **  The help volume MUST not be writable by group or others to be trusted.
261    ** ----------------------------------------------------------------------- */
262
263    writable = (((buf.st_mode & S_IWGRP) == 0) && (buf.st_mode & S_IWOTH) == 0) ?  
264               True : False;
265
266    return writable;
267
268 }
269
270
271 /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
272 /****       API Error Dialog Support Functions       *****/
273 /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
274
275
276 /************************************************************************
277  * Function: _DtMessageClose
278  *
279  *      Close the error/info message box.
280  *
281  ************************************************************************/ 
282 static void _DtMessageClose(
283     Widget w,
284     XtPointer client_data,
285     XEvent *event )
286 {                        
287    /* NOTE: ExecuteContextCB() is dependent on this code */
288    if (event->type == UnmapNotify)
289    {
290       XtRemoveEventHandler (XtParent (client_data), StructureNotifyMask, 
291                             True, (XtEventHandler) _DtMessageClose, client_data);
292
293       XtUnmanageChild (client_data);
294       XtDestroyWidget (client_data);
295    }
296 }
297
298 /************************************************************************
299  * Function: ExecuteContextCB
300  *
301  *      Execute an execution context
302  *
303  ************************************************************************/ 
304 static void ExecuteContextCB(
305     Widget w,
306     XtPointer client_data,
307     XtPointer callData )
308 {
309     ExecContext * ec = (ExecContext *) client_data;
310
311     if (ec && ec->command && ec->pDisplayArea)
312     {
313         _DtHelpExecProcedure(ec->pDisplayArea,ec->command);
314         free(ec->command);
315     }
316     XtFree((char *) ec);
317
318     /* unmap, rather than unmanage and destroy, because of the code
319        in _DtMessageClose().  _DtMessageClose() is notified when 
320        the widget unmaps and it destroys the widget. */
321     XtUnmapWidget(w);       /* w is the message dialog */
322 }
323
324 /*****************************************************************************
325  * Function: CreateErrorDialog
326  *
327  *  Creates an XmMessageDialog with the message and all buttons
328  *  except the 'Close' (OK) button unmanaged.
329  *  Also adds a callback that destroys the widget when the dialog is closed.
330  * 
331  *****************************************************************************/
332 static Widget 
333 CreateErrorDialog(
334     Widget                   parent,
335     char *                   message)
336 {     
337    Widget       button;
338    Widget       messageDialog;
339    Arg          args[10];
340    int          n;
341  
342    XmString     label_string;
343    XmString      ok_string;
344    char          *title_string; 
345  
346    /* Setup the message string and dialog title */
347  
348    ok_string = XmStringCreateLocalized(((char *)_DTGETMESSAGE
349                                (HUSET, 2,"Close")));
350    label_string = XmStringCreateLocalized(message);
351    title_string = XtNewString((char *)_DTGETMESSAGE
352                               (HUSET, 5,"Help Error"));
353  
354    n = 0;
355    XtSetArg (args[n], XmNmessageString, label_string);  n++;
356    XtSetArg (args[n], XmNtitle,title_string); n++;
357    XtSetArg (args[n], XmNcancelLabelString, ok_string); n++;
358    XtSetArg (args[n], XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON); n++;
359    messageDialog = XmCreateErrorDialog (parent, "errorDialog",
360                                                args, n);
361    XtSetArg(args[0], XmNmwmDecorations,
362                               MWM_DECOR_BORDER | MWM_DECOR_TITLE);
363    XtSetArg(args[1], XmNuseAsyncGeometry, True);
364    XtSetValues(XtParent(messageDialog), args, 2);
365  
366    XmStringFree (label_string);
367    XmStringFree (ok_string);
368    XtFree(title_string);
369  
370    /* unmanage or define the other buttons */
371    button = XmMessageBoxGetChild (messageDialog, XmDIALOG_OK_BUTTON);
372    XtUnmanageChild (button);
373    button = XmMessageBoxGetChild (messageDialog, XmDIALOG_HELP_BUTTON);
374    XtUnmanageChild (button);
375  
376    /* StructureNotifyMask gets Circulate, Configure, Destroy, 
377       Gravity, Map, Reparent, & Unmap events */
378    XtAddEventHandler(XtParent(messageDialog),
379                      StructureNotifyMask,True,
380                      (XtEventHandler) _DtMessageClose, (XtPointer) messageDialog);
381  
382    return messageDialog;           /* RETURN */
383
384 }
385
386 /*****************************************************************************
387  * Function: CreateExecErrorDlg
388  *
389  * 
390  * 
391  * Called by:
392  *****************************************************************************/
393 static Widget
394 CreateExecErrorDlg(
395       Widget   helpWidget,
396       const char *  cmdStr,
397       Boolean  invalidAlias,
398       _DtHelpCommonHelpStuff * pHelpStuff,
399       int        condition,
400       char     * current_hd)
401 {
402    DtHelpListStruct *pHelpInfo;
403    Widget msgDlg;
404    Widget btn;
405    char *  msg;
406    char *  fullmsg;
407
408    /* handle the error case */
409    if (invalidAlias)
410    {
411       msg = (char *)_DTGETMESSAGE(HUSET, 12,
412                "The help volume wanted to execute a command alias.\n"
413                "The alias '%s' is not defined.");
414    }
415
416    else if (condition == MISMATCHING_HOME_DIRS)
417    {
418       msg = (char *)_DTGETMESSAGE(HUSET, 14,
419 "The help volume wanted to execute a command as the root user, but the\n"
420 "home directory of  \"%s\"  ($HOME) does not match the root\n"
421 "user's home directory.  This could result in executing unexpected\n"
422 "commands.\n\n"
423
424 "The command is:  \"%s\"\n\n"
425
426 "Note:  to avoid this in the future:\n"
427 "  execute \"su - root\"  rather than \"su root\".\n");
428    }
429
430    else
431    {
432       msg = (char *)_DTGETMESSAGE(HUSET, 13,
433              "The help volume wanted to execute a command.\n"
434              "For security reasons, automatic command execution is turned off.\n"
435              "The command is:  %s");
436    }
437    fullmsg = (char *) malloc(strlen(msg)+strlen(cmdStr)+30);
438    if (fullmsg) 
439    {
440       if (condition == MISMATCHING_HOME_DIRS)
441          sprintf(fullmsg, msg, current_hd, cmdStr);
442       else
443          sprintf(fullmsg,msg,cmdStr);
444    }
445    else 
446       fullmsg = msg;
447
448    /* create an error dialog, but don't manage it yet */
449    msgDlg = CreateErrorDialog(XtParent(helpWidget),fullmsg);
450    
451    if (msg != fullmsg) free(fullmsg);
452
453    btn = XmMessageBoxGetChild (msgDlg, XmDIALOG_HELP_BUTTON);
454    XtManageChild (btn);           /* re-manage the button */
455
456    /* add the HelpOnHelp callback */
457    pHelpInfo = _DtHelpListAdd(DtHELP_ExecutionPolicy_STR,
458                         helpWidget, pHelpStuff, &pHelpStuff->pHelpListHead);
459    XtAddCallback(btn, XmNactivateCallback, _DtHelpCB, (XtPointer) pHelpInfo);
460
461    return msgDlg;
462 }
463  
464 /*****************************************************************************
465  * Function: _DtHelpErrorDialog
466  *
467  * 
468  * 
469  * Called by:
470  *****************************************************************************/
471 void _DtHelpErrorDialog(
472     Widget                   parent,
473     char *                   message)
474 {     
475   Widget messageDialog;
476
477   messageDialog = CreateErrorDialog(parent,message);
478
479   /* Display help window. This used to be before the call 
480      to add a StructureNotify event handler */
481   XtManageChild (messageDialog);
482 }
483
484
485 /*****************************************************************************
486  * Function: _DtHelpFilterExecCmdStr
487  *
488  *   Args:
489  *    helpWidget:       help widget requesting to exec the command
490  *    pDisplayStuff:    ptr to the DisplayWidget stuff of the help widget
491  *    commandStr:       command string to execute
492  *    ret_cmdStr:       the screened & possibly rewritten command is put here
493  *    ret_invalidAlias:  was the command an invalid alias?
494  *    ret_execPermitted: if executionPolicy permit exec & ret_cmdStr is valid
495  *    ret_queryNeeded:  if executionPolicy requires a query before exec
496  *
497  * Description:
498  *    ret_cmdStr gets memory owned by the calling function; it should be
499  *    freed when no longer needed.  The string will be the same as the
500  *    commandStr if commandStr was not an alias.  If the commandStr
501  *    is an alias and if the alias is defined, the ret_cmdStr will be the
502  *    value of the alias.  If the alias isn't defined, the ret_cmdStr will
503  *    be the default command if available, or the alias name otherwise.
504  * 
505  *    ret_invalidAlias will be True if the alias was undefined and
506  *    no default command was given.
507  *
508  *    ret_execPermitted will be True if executionPolicy is DtHELP_EXECUTE_ALL
509  *    or DtHELP_EXECUTE_QUERY_xxx and ret_cmdStr is valid
510  *
511  *    ret_queryNeeded will be True if executionPoilcy is 
512  *    DtHELP_EXECUTE_QUERY_ALL or if it is DtHELP_EXECUTE_QUERY_UNALIASED 
513  *    and ret_cmdStr did not derive from an alias (i.e. was hardcoded
514  *    in the help volume, not retrieved from a resource).
515  *
516  * Returns:
517  *    True:  if execPermitted and a valid command string
518  *    False: if if execPermitted is False or invalid command string
519  *
520  * Comments:
521  *    This code is written such that we don't need nor want to know
522  *    whether it is a general or quick help widget.
523  * 
524  * Warning:
525  *    command string must be writable; it is written, but left
526  *    unchanged when the function exits.
527  *
528  *****************************************************************************/
529 Boolean _DtHelpFilterExecCmdStr(
530     Widget                   helpWidget,
531     unsigned char            executionPolicy,
532     const char *             commandStr,
533     char * *                 ret_cmdStr,
534     Boolean *                ret_invalidAlias,
535     Boolean *                ret_execPermitted,
536     Boolean *                ret_queryNeeded,
537     char *                   hv_path)     /* Path to the Help Volume */
538 {     
539    char * token;
540    char * tokenEnd;
541    char   tokenEndChar;
542    char * aliasCommand = NULL;
543    Boolean ret;
544
545 #define RN_execAlias "executionAlias"
546 #define RC_execAlias "ExecutionAlias"
547 #define ExecAliasCmd "DtHelpExecAlias"
548
549    /* default values */
550    *ret_cmdStr = NULL;
551    *ret_invalidAlias = False;
552    *ret_execPermitted = False;
553    *ret_queryNeeded = False;
554
555    if (NULL == commandStr) 
556       return False;
557
558   /** ------------------------------------------------------------- *
559    **  If the executionPolicy is query all unaliased (query for all
560    **  execution links that have no execution alias defined), we 
561    **  make an exception:  only query the user for help volumes 
562    **  deemed NOT "trusted".
563    ** ------------------------------------------------------------- */
564
565    if (DtHELP_EXECUTE_QUERY_UNALIASED == executionPolicy)
566    {
567       if ( ! (trusted (hv_path)))
568          *ret_queryNeeded = True;   /* Query ALL non-trusted help volumes */
569    }
570    else
571       *ret_queryNeeded = (DtHELP_EXECUTE_QUERY_ALL == executionPolicy);
572
573    /* get whether exec permitted */
574    if (   DtHELP_EXECUTE_ALL == executionPolicy 
575        || DtHELP_EXECUTE_QUERY_UNALIASED == executionPolicy
576        || DtHELP_EXECUTE_QUERY_ALL == executionPolicy)
577       *ret_execPermitted = True;
578    else
579       *ret_execPermitted = False;
580
581    /* parse apart the command string, looking for DtHelpExecAlias */
582    /* The first call will return true, with the first string */
583    token = (char *) commandStr + DtStrspn((char *)commandStr, " \t");
584    tokenEnd = token + DtStrcspn(token, " \t");
585    tokenEndChar = *tokenEnd;
586    if (tokenEnd) *tokenEnd = EOS;
587
588    if ( NULL == token || _DtHelpCeStrCaseCmpLatin1(token, ExecAliasCmd) != 0 )
589    {
590       /*** the command is not an alias; proceed using execution Policy ***/
591    
592       *tokenEnd = tokenEndChar;       /* restore the string */
593    
594       *ret_cmdStr = strdup(commandStr);
595       ret = *ret_execPermitted;
596
597       return ret;                             /* RETURN ret */
598    }
599
600
601    /**** It's an alias; get it , look it up, and return it ****/
602
603    *tokenEnd = tokenEndChar;       /* restore the string */
604
605    /* get the next token */
606    token = tokenEnd + DtStrspn(tokenEnd, " \t");
607    tokenEnd = token + DtStrcspn(token, " \t");
608    tokenEndChar = *tokenEnd;
609    if (tokenEnd) *tokenEnd = EOS;
610
611    if ( token )
612    {
613       Display * dpy = XtDisplay(helpWidget);
614       XrmDatabase appDb = XrmGetDatabase(dpy);
615       XrmValue value;
616       String appname, appclass;
617       char * reptype;
618       char *rsrc_name, *rsrc_class;
619
620       rsrc_name = XtMalloc(200);
621       rsrc_class = XtMalloc(200);
622
623       XtGetApplicationNameAndClass(dpy,&appname,&appclass);
624
625       /* query the application's database for the alias command */
626       /* build alias resource class and resource */
627       /* e.g. App.executionAlias.<alias> */
628       sprintf(rsrc_name,"%s.%s.%s",appclass, RN_execAlias,token);
629       /* e.g. App.ExecutionAlias.<alias> */
630       sprintf(rsrc_class,"%s.%s.%s",appclass, RC_execAlias,token);
631
632       /* Get alias command */
633       if (XrmGetResource(appDb,rsrc_name,rsrc_class,&reptype,&value) == True)
634          aliasCommand = value.addr;
635
636       /* build alias resource name and resource */
637       /* e.g. app.executionAlias.<alias> */
638       sprintf(rsrc_name,"%s.%s.%s",appname, RN_execAlias,token);
639       /* e.g. app.ExecutionAlias.<alias> */
640       sprintf(rsrc_class,"%s.%s.%s",appname, RC_execAlias,token);
641
642       /* Get alias command and override class with instance, if defined */
643       if (XrmGetResource(appDb,rsrc_name,rsrc_class,&reptype,&value) == True)
644          aliasCommand = value.addr;
645
646       if (rsrc_name) XtFree(rsrc_name);
647       if (rsrc_class) XtFree(rsrc_class);
648    }  /* if alias token */
649    else 
650    {
651       token = "";
652    }
653
654    if (tokenEnd) *tokenEnd = tokenEndChar; /* restore the string */
655
656    /* alias was defined */
657    if (aliasCommand) 
658    {
659       *ret_cmdStr = strdup(aliasCommand);
660       /* see if query needed; is not if policy is query_unaliased or all */
661       *ret_queryNeeded = !(   DtHELP_EXECUTE_QUERY_UNALIASED == executionPolicy
662                            || DtHELP_EXECUTE_ALL == executionPolicy);
663       ret = *ret_execPermitted;
664    }
665    else /* the alias wasn't defined */
666    {
667       char * aliasToken = token;   /* token currently pts to alias */
668
669       /* look for a default command */
670       /* get the next token */
671       token = tokenEnd + DtStrspn(tokenEnd, " \t");
672       tokenEnd = token + DtStrcspn(token, " \t");
673
674       if (token == tokenEnd)
675       {  /* alias wasn't defined and no default command */
676          *ret_cmdStr = strdup(aliasToken);
677          *ret_invalidAlias = True;
678          *ret_queryNeeded = False;  /* no query needed on invalid alias, ever */
679          *ret_execPermitted = False; /* can't exec an invalid alias */
680          ret = False;
681       }
682       else 
683       {  /* alias wasn't defined but a default command */
684          /* query is whatever was determined earlier */
685          *ret_cmdStr = strdup(token);
686          ret = *ret_execPermitted;
687       }
688    }
689
690    return ret;                          /* RETURN ret */
691 }
692
693 /*********************************************************************
694  * _DtHelpCeWaitAndProcessEvents
695  *
696  *  Purpose:
697  *    _DtHelpCeWaitAndProcessEvents will process events and call
698  *    the waitProc until waitProc returns False.   This function
699  *    is useful to put up modal dialogs that must be reponded to 
700  *    in the midst of executing code that must remain on the call stack.
701  *
702  *  Warning:
703  *    This function should only be used on modal dialogs.
704  *
705  *********************************************************************/
706
707 void
708 _DtHelpCeWaitAndProcessEvents (
709     Widget             w,
710     _DtHelpCeWaitProc  waitProc,
711     void *             clientData)
712 {
713     Boolean waitFlag;
714     XEvent   event;
715     XtAppContext app;
716
717     app = XtWidgetToApplicationContext(w);
718     do 
719     {
720 #ifndef XTHREADS
721         XtAppNextEvent(app,&event);
722         XtDispatchEvent(&event);
723 #else
724         XtInputMask mask;
725
726         while (!(mask = XtAppPending(app)))
727           ;   /* Busy waiting - so we don't lose our Lock! */
728         
729         if (mask & XtIMXEvent)         /* We have an XEvent */
730           {
731             /* Get the XEvent - we know it's there! Note that XtAppNextEvent
732                would also process timers/alternate inputs.
733              */
734             XtAppNextEvent(app, &event);  /* No blocking, since an event is ready */
735             XtDispatchEvent(&event);
736           }
737         else   /* Not a XEvent, it's an alternate input/timer event */
738           {
739             XtAppProcessEvent(app, mask); /* No blocking, since an event is ready */
740           }
741 #endif /* XTHREADS */
742         /* check to see if we're done waiting */
743         waitFlag = (*waitProc)(w, clientData); 
744     } while (waitFlag);
745 }
746
747 /*****************************************************************************
748 * Function: WaitForBtnActivatedCB
749 *
750 * Purpose:
751 *    Treats the 'clientData' as a pointer to an integer
752 *    and turns its value into a Boolean
753 *
754 * Returns:  *(int *)clientData < 0
755 *****************************************************************************/
756 static Boolean 
757 WaitForBtnActivatedCB(
758     Widget w,
759     void * clientData)
760 {
761     return (*(int *)clientData < 0);
762    /* True=keep waiting; False= wait no longer */
763 }
764
765
766 typedef struct ModalMsgDlgCBStruct
767 {
768   Widget msgDlg;
769   Widget okBtn;
770   Widget cancelBtn;
771   Widget helpBtn;
772   int    activatedBtnId;
773 } ModalMsgDlgCBStruct;
774
775 /*****************************************************************************
776 * Function: IdentifyActivatedBtnCB
777 *
778 * Purpose:
779 *    Treats the 'clientData' as a pointer to an integer.
780 *    Waits for the value pointed to by clientData to be >= 0.
781 *
782 *****************************************************************************/
783 static void 
784 IdentifyActivatedBtnCB(
785     Widget w,
786     XtPointer clientData,
787     XtPointer callData)
788 {
789    ModalMsgDlgCBStruct * pMd = (ModalMsgDlgCBStruct *) clientData;
790     
791    /* w must be a XmMessageDialog widget */
792    if (pMd->okBtn == w) 
793       { pMd->activatedBtnId = XmDIALOG_OK_BUTTON; return; /* RETURN */ }
794    if (pMd->cancelBtn == w) 
795       { pMd->activatedBtnId = XmDIALOG_CANCEL_BUTTON; return; /* RETURN */ }
796    if (pMd->helpBtn == w) 
797       { pMd->activatedBtnId = XmDIALOG_HELP_BUTTON; return; /* RETURN */ }
798    pMd->activatedBtnId = -1;      /* unknown button */
799 }
800     
801
802 /*****************************************************************************
803  * Function: _DtHelpFilterExecCmd
804  *
805  *   Args:
806  *    helpWidget:       help widget requesting to exec the command
807  *    command:          command string to execute
808  *    execPolicy:       current policy setting
809  *    useQueryDialog:   use a dialog to query user whether to exec, if not allowed
810  *    pHelpStuff:       ptr to the HelpStuff structure of the help widget
811  *    ret_filteredCmdStr: filtered command string
812  * 
813  * Returns:
814  *    0: no error; filteredCmdStr can be exec'd
815  *   -1: error: either internal or executionPolicy denies exec; 
816  *       filteredCmdStr is NULL
817  *
818  * Purpose:
819  *    This function filters an execution command string.  This can
820  *    occur in several ways.  In all cases, the command string is
821  *    supports command alias replacement.  If the final outcome
822  *    is that execution is permitted, the returned string is
823  *    is the command string to execute.  If execution is not
824  *    permitted, the return string is a NULL pointer. 
825  *
826  *    Filtering of the command occurs as follows.
827  *    If executionPolicy permits execution, only alias replacement occurs.
828  *    If executionPolicy does restrict execution and a
829  *    dialog is requested, then a modal dialog is posted and the
830  *    user can decide whether to execute or not.
831  *    If a dialog is not requested, the return string is NULL.
832  *
833  * Comments:
834  *    This code is written such that we don't need nor want to know
835  *    whether it is a general or quick help widget.
836  * 
837  * Warning:
838  *    command string must be writable; it is written, but left
839  *    unchanged whent the function exits.
840  *
841  *    This operation is synchronous, meaning that, if a dialog is
842  *    posted, it is a modal dialog and the function won't return 
843  *    until the user selects a button.
844  *
845  * Called by:
846  *****************************************************************************/
847 int _DtHelpFilterExecCmd(
848     Widget        helpWidget,
849     const char *  commandStr,
850     unsigned char executionPolicy,
851     Boolean       useQueryDialog,
852     _DtHelpCommonHelpStuff * pHelpStuff,
853     char * *      ret_filteredCmdStr,
854     char *                   hv_path)
855 {     
856    ModalMsgDlgCBStruct msgDlgCBStruct;
857    Boolean goodCmd;
858    Boolean invalidAlias;
859    Boolean execPermitted;
860    Boolean queryNeeded;
861    Widget  msgDlg;
862    char *  filteredCmdStr = NULL;
863    Arg     args[5];
864    int     n;
865    XmString labelString;
866    XmString labelString2;
867    Widget  noexecBtn;
868    Widget  execBtn;
869    Cursor  cursor;
870
871    goodCmd = _DtHelpFilterExecCmdStr(helpWidget, executionPolicy, 
872                  commandStr, &filteredCmdStr, &invalidAlias, 
873                  &execPermitted, &queryNeeded, hv_path);
874
875    /* if permissions allow immediate execution, do so */
876    if (execPermitted && False == queryNeeded)
877    {
878       *ret_filteredCmdStr = filteredCmdStr;
879       return 0;                           /* RETURN ok */
880    }
881
882    if (False == useQueryDialog)
883    {
884       *ret_filteredCmdStr = NULL;
885       return -1;                          /* RETURN error */
886    }
887
888    /* create the dialog, but don't yet manage it */
889    msgDlg =  CreateExecErrorDlg(helpWidget,filteredCmdStr,
890                         invalidAlias,pHelpStuff, NO_CONDITION, "");
891
892    /* if a bad alias or no exec permitted, 
893       don't need to wait for a response; dlg has close & Help */
894    if (False == execPermitted || False == queryNeeded)  
895    {
896       XtManageChild(msgDlg);                 /* manage modeless dialog */
897       *ret_filteredCmdStr = NULL;            /* don't execute */
898       XtFree(filteredCmdStr);
899       return -1;                            /* RETURN error */
900    }
901
902    /* if got this far, query is needed;make the dialog include 
903       Execute Anyway and Don't Execute buttons */
904
905    /* give the right title to the buttons */
906    labelString = XmStringCreateLocalized(((char *)_DTGETMESSAGE
907                    (HUSET, 10,"Execute Anyway")));
908    /* give the right title to the Cancel button */
909    labelString2 = XmStringCreateLocalized(((char *)_DTGETMESSAGE
910                    (HUSET, 11,"Don't Execute")));
911    n = 0;
912    XtSetArg (args[n], XmNokLabelString, labelString);   n++;
913    XtSetArg (args[n], XmNcancelLabelString, labelString2);              n++;
914    XtSetArg (args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); n++;
915    XtSetValues(msgDlg,args,n);
916    XmStringFree(labelString);
917    XmStringFree(labelString2);
918
919    /* We put an activate callback on the DontExecute and ExecuteAnyway buttons 
920       and wait until a button is pressed.  */
921    noexecBtn = XmMessageBoxGetChild (msgDlg, XmDIALOG_CANCEL_BUTTON);
922    XtAddCallback(noexecBtn, XmNactivateCallback, 
923                              IdentifyActivatedBtnCB, (XtPointer) &msgDlgCBStruct);
924
925    execBtn = XmMessageBoxGetChild (msgDlg, XmDIALOG_OK_BUTTON);
926    XtManageChild (execBtn);           /* re-manage the button */
927    XtAddCallback(execBtn, XmNactivateCallback, 
928                              IdentifyActivatedBtnCB, (XtPointer) &msgDlgCBStruct);
929    
930    /* fill out the CB information structure used by IdentifyActivatedBtnCB */
931    msgDlgCBStruct.msgDlg = msgDlg;
932    msgDlgCBStruct.okBtn = execBtn;
933    msgDlgCBStruct.cancelBtn = noexecBtn;
934    msgDlgCBStruct.activatedBtnId = -1;
935
936    /* Display message dialog */
937    XtManageChild (msgDlg);
938
939    /*
940     * turn on the modal dialog indicator
941     */
942    _DtHelpTurnOnNoEnter(helpWidget);
943
944    /* wait until 'msgDlgCBStruct.activatedBtnId' has a value >= 0 */
945    /* this occurs when the user responds to the msg dialog */
946    _DtHelpCeWaitAndProcessEvents(msgDlg, 
947               WaitForBtnActivatedCB, &msgDlgCBStruct.activatedBtnId);
948
949    /*
950     * turn off the modal dialog indicator
951     */
952    _DtHelpTurnOffNoEnter(helpWidget);
953
954    /* no need to destroy msgDlg; it has a closeCallback to do that */
955
956    /* act based on which button was activated */
957    if (msgDlgCBStruct.activatedBtnId == XmDIALOG_OK_BUTTON)
958    {
959       *ret_filteredCmdStr = filteredCmdStr;  /* do execute command */
960       return 0;                             /* RETURN ok */
961    }
962    else
963    {
964       *ret_filteredCmdStr = NULL;            /* don't execute */
965       XtFree(filteredCmdStr);
966       return -1;                            /* RETURN error */
967    }
968 }
969
970 /*****************************************************************************
971  * Function: _DtHelpExecFilteredCmd
972  *
973  *   Args:
974  *    helpWidget:       help widget requesting to exec the command
975  *    command:          command string to execute
976  *    modal:            is the execution modal (sync) or modeless (async)
977  *    helpLocationId:   helpOnHelp file location for Help btn in error dialog
978  *    pDisplayStuff:    ptr to the DisplayWidget stuff of the help widget
979  *    pHelpStuff:       ptr to the CommonHelp stuff of the help widget
980  * 
981  * Comments:
982  *    This code is written such that we don't need nor want to know
983  *    whether it is a general or quick help widget.
984  * 
985  * Warning:
986  *    command string must be writable; it is written, but left
987  *    unchanged whent the function exits.
988  *
989  *    At the moment, the helpLocationId is ignored, and the
990  *    help location is hardwired to DtHELP_ExecutionPolicy_STR
991  *    in CreateExecErrorDialog().
992  * Called by:
993  *****************************************************************************/
994 void _DtHelpExecFilteredCmd(
995     Widget                   helpWidget,
996     char *                   commandStr,
997     char *                   helpLocationId,
998     _DtHelpDisplayWidgetStuff * pDisplayStuff,
999     _DtHelpCommonHelpStuff * pHelpStuff)
1000 {     
1001    Boolean goodCmd;
1002    Boolean invalidAlias;
1003    Boolean execPermitted;
1004    Boolean queryNeeded;
1005    char *  filteredCmdStr = NULL;
1006    ExecContext * execContext;
1007    DtHelpListStruct *pHelpInfo;
1008    XmString labelString;
1009    XmString labelString2;
1010    Widget   msgDlg;
1011    Widget   btn;
1012    int     n;   
1013    Arg     args[5];
1014    char    *hv_path=NULL;
1015
1016    uid_t         user;
1017    char          *home_dir;
1018    Boolean        diff_home_dirs=False; /* True ==> $HOME is different from */
1019                                         /* root user's $HOME directory      */
1020 /*
1021    getpw{uid,nam}_r routines fail on IBM platform when search password info
1022    via NIS (yellow pages). However, in the case of root, we'll assume that
1023    the password info is in /etc/passwd. If this is not the case, the
1024    following code can fail on IBM platform when XTHREADS and XUSE_MTSAFE_API
1025    are defined.
1026 */
1027    _Xgetpwparams pwd_buf;
1028    struct passwd * pwd_ret;
1029
1030   /** -------------------------------------------------------------- *
1031    **  If we're running as the root user
1032    **   o check if the value of the HOME env var matches 
1033    **     root's home directory (defined by /etc/passwd).
1034    **   o If they do not match, then present a dialog 
1035    **     alerting the user of this, along with the command to 
1036    **     invoke.
1037    ** -------------------------------------------------------------- */
1038
1039    if ( (user=getuid()) == ROOT_USER)
1040    {
1041       home_dir = getenv ("HOME");
1042
1043       if (home_dir != NULL && strlen(home_dir) >= (size_t) 1)
1044       {
1045          if (((pwd_ret = _XGetpwuid(user, pwd_buf)) != NULL)
1046                          && (strcmp(home_dir, pwd_ret->pw_dir)))
1047          {
1048             diff_home_dirs = True;
1049          }
1050       }
1051    }
1052
1053    hv_path = _DtHelpFileLocate(DtHelpVOLUME_TYPE, pDisplayStuff->helpVolume,
1054                                _DtHelpFileSuffixList, False, R_OK);
1055
1056    /* The desired and intended behaviour is to use _DtHelpFilterExecCmdStr(), but
1057       the other code is left here, should a change be wished. */
1058 #if 1
1059    /* This function runs a filter for policy and alias but posts no dialog  */
1060    goodCmd=_DtHelpFilterExecCmdStr(helpWidget, 
1061               pDisplayStuff->executionPolicy, commandStr, 
1062                &filteredCmdStr, &invalidAlias, &execPermitted, &queryNeeded, hv_path);
1063 #else
1064     /* This function does an synchronous filter; i.e. the code runs a filter
1065        for policy and alias, and if policy denies exec and the command is
1066        valid, then posts a modal dialog and waits for the user to decide
1067        what to do before returning. */
1068    goodCmd = _DtHelpFilterExecCmd(helpWidget, commandStr, 
1069                                   pDisplayStuff->executionPolicy, True, 
1070                                   pHelpStuff, &filteredCmdStr, hv_path);
1071    execPermitted = (goodCmd == 0);  /* convert an error int into a Boolean */
1072    queryNeeded =    
1073           ((   pDisplayStuff->executionPolicy==DtHELP_EXECUTE_QUERY_ALL)
1074             || (pDisplayStuff->executionPolicy==DtHELP_EXECUTE_QUERY_UNALIASED))
1075        && goodCmd;
1076 #endif
1077
1078    /* if permissions allow immediate execution, do so */
1079    if (execPermitted && False == queryNeeded  && diff_home_dirs == False)
1080    {
1081       (void) _DtHelpExecProcedure (pHelpStuff->pDisplayArea, filteredCmdStr);
1082       free(filteredCmdStr);
1083       return;                           /* RETURN */
1084    }
1085
1086    /* this traps bad cmds and also use of the synchronous filter call */
1087    if (NULL == filteredCmdStr) return;       /* RETURN */
1088
1089    /*** Create a modeless dialog to inform the user of the problem
1090         and possibly allow them to execute the command anyway. ***/
1091
1092    /* create the dialog, but don't yet manage it */
1093    if ( diff_home_dirs == True)
1094      msgDlg =  CreateExecErrorDlg(helpWidget,filteredCmdStr, invalidAlias,pHelpStuff, 
1095                                   MISMATCHING_HOME_DIRS, home_dir );
1096    else
1097      msgDlg =  CreateExecErrorDlg(helpWidget,filteredCmdStr, invalidAlias,pHelpStuff, 
1098                                   NO_CONDITION, "");
1099
1100
1101    /*** setup ExecuteAnyway and Help buttons ***/
1102
1103    if ( (diff_home_dirs == True)  
1104                      ||
1105         (queryNeeded && execPermitted) )
1106    {
1107       /* give the right title to the buttons */
1108       labelString = XmStringCreateLocalized(((char *)_DTGETMESSAGE
1109                          (HUSET, 10,"Execute Anyway")));
1110       /* give the right title to the Cancel button */
1111       labelString2 = XmStringCreateLocalized(((char *)_DTGETMESSAGE
1112                       (HUSET, 11,"Don't Execute")));
1113       n = 0;
1114       XtSetArg (args[n], XmNokLabelString, labelString);        n++;
1115       XtSetArg (args[n], XmNcancelLabelString, labelString2);   n++;
1116       XtSetValues(msgDlg,args,n);
1117       XmStringFree(labelString);
1118       XmStringFree(labelString2);
1119    
1120       btn = XmMessageBoxGetChild (msgDlg, XmDIALOG_OK_BUTTON);
1121       XtManageChild (btn);           /* re-manage the button */
1122    
1123       /* add the ExecuteContextCB() client-data and callback */
1124       execContext = malloc(sizeof(ExecContext));
1125       if (execContext) 
1126       { 
1127          execContext->command = filteredCmdStr;
1128          execContext->pDisplayArea = pHelpStuff->pDisplayArea;
1129          XtAddCallback(btn, XmNactivateCallback, 
1130                              ExecuteContextCB, (XtPointer) execContext);
1131       }
1132       else
1133       {
1134          free(filteredCmdStr);
1135       }
1136    }  /* cmd wasn't an alias */
1137    else
1138    {
1139       free(filteredCmdStr);
1140    }
1141
1142    /* Display message dialog */
1143    XtManageChild (msgDlg);
1144
1145    /* RETURN */
1146 }
1147
1148
1149
1150 /*****************************************************************************
1151  * Function: LocateWidgetId()
1152  *
1153  *   
1154  *
1155  * Called by: 
1156  *****************************************************************************/
1157 static Widget  LocateWidgetId(
1158     Display          *dpy,
1159     int              screen,
1160     int             *statusRet,
1161     Widget           shellWidget,
1162     Cursor           cursorIn)
1163 {
1164     static Cursor    DfltOnItemCursor = NULL;
1165     Widget           widget;
1166     Widget           child;
1167     CompositeWidget  comp_widget;
1168     int              status;
1169     Cursor           cursor;
1170     XEvent           event;
1171     int              x,y;
1172     int              i;
1173     Window           parent;
1174     Window           sub;
1175     Window           target_win;
1176     int              new_x, new_y;
1177     int              offset;
1178     KeySym           keySym;
1179     Boolean          notDone=TRUE;
1180
1181  
1182     /* Make the target cursor */
1183     if (cursorIn != NULL)
1184         cursor = cursorIn;
1185     else
1186 #if 0
1187         cursor = XCreateFontCursor (dpy, XC_question_arrow);
1188 #else
1189     {
1190         _DtHelpProcessLock();
1191         if (NULL == DfltOnItemCursor)
1192         {
1193             char        *bits;
1194             char        *maskBits;
1195             unsigned int width;
1196             unsigned int height;
1197             unsigned int xHotspot;
1198             unsigned int yHotspot;
1199             Pixmap       pixmap;
1200             Pixmap       maskPixmap;
1201             XColor       xcolors[2];
1202    
1203             width    = onitem32_width;
1204             height   = onitem32_height;
1205             bits     = (char *) onitem32_bits;
1206             maskBits = (char *) onitem32_m_bits;
1207             xHotspot = onitem32_x_hot;
1208             yHotspot = onitem32_y_hot;
1209     
1210             pixmap = XCreateBitmapFromData (dpy,
1211                          RootWindowOfScreen(XtScreen(shellWidget)), bits,
1212                          width, height);
1213     
1214     
1215             maskPixmap = XCreateBitmapFromData (dpy,
1216                          RootWindowOfScreen(XtScreen(shellWidget)), maskBits,
1217                          width, height);
1218     
1219             xcolors[0].pixel = BlackPixelOfScreen(ScreenOfDisplay(dpy, screen));
1220             xcolors[1].pixel = WhitePixelOfScreen(ScreenOfDisplay(dpy, screen));
1221     
1222             XQueryColors(dpy,
1223                          DefaultColormapOfScreen(ScreenOfDisplay(dpy, screen)), 
1224                          xcolors, 
1225                          2);
1226     
1227             DfltOnItemCursor = XCreatePixmapCursor (dpy, pixmap, maskPixmap,
1228                                           &(xcolors[0]), &(xcolors[1]),
1229                                           xHotspot, yHotspot);
1230             XFreePixmap (dpy, pixmap);
1231             XFreePixmap (dpy, maskPixmap);
1232         }  /* if dflt cursor not yet created */
1233         cursor = DfltOnItemCursor;
1234         _DtHelpProcessUnlock();
1235     }  /* if to use the standard cursor */
1236 #endif
1237
1238
1239     /* Grab the pointer using target cursor, letting it roam all over */
1240     status = XtGrabPointer (shellWidget, TRUE,
1241                            ButtonPressMask|ButtonReleaseMask, GrabModeAsync,
1242                            GrabModeAsync, None, cursor, CurrentTime);
1243     if (status != GrabSuccess)
1244       {
1245         XmeWarning(shellWidget,(char *)_DTGETMESSAGE(HUSET, 3,
1246               "Internal Error: Could not grab the mouse\nDtHelpReturnSelectedWidget aborted.\n"));
1247         *statusRet = DtHELP_SELECT_ERROR;
1248         return(NULL);
1249       }
1250
1251
1252
1253     /* Grab the Keyboard so we can catch the ESC button press */
1254     status = XtGrabKeyboard(shellWidget, False,
1255                            GrabModeAsync, GrabModeAsync, CurrentTime);
1256     if (status != GrabSuccess)
1257       {
1258         
1259         XtUngrabPointer (shellWidget, CurrentTime);
1260         XmeWarning(shellWidget,(char *)_DTGETMESSAGE(HUSET, 4,
1261         "Internal Error: Could not grab the keyboard\nDtHelpReturnSelectedWidget() aborted.\n"));
1262         *statusRet = DtHELP_SELECT_ERROR;
1263         return(NULL);
1264       }
1265
1266     /* We are ok so let the user select a window... */
1267     while (notDone)
1268     {
1269       XtAppContext app = XtWidgetToApplicationContext(shellWidget);
1270       /* allow one more event */
1271 #ifndef XTHREADS
1272       XtAppNextEvent(app, &event);
1273 #else
1274       XtInputMask mask;
1275
1276       while (!(mask = XtAppPending(app)))
1277           ;   /* Busy waiting - so we don't lose our Lock! */
1278         
1279       if (!(mask & XtIMXEvent)) /* Not a XEvent, it's an alternate input/timer event */
1280             XtAppProcessEvent(app, mask); /* No blocking, since an event is ready */
1281       else   /* We have an XEvent */
1282         {
1283             /* Get the XEvent - we know it's there! Note that XtAppNextEvent
1284                would also process timers/alternate inputs.
1285              */
1286             XtAppNextEvent(app, &event);
1287 #endif /* XTHREADS */
1288       widget = XtWindowToWidget(dpy, event.xany.window);
1289       
1290       switch (event.type) {
1291             case ButtonPress:
1292                 break;
1293             case ButtonRelease:
1294                 notDone = FALSE;
1295                 break;
1296             case KeyPress:
1297                 /* Look for ESC key press and stop if we get one */
1298                 if (event.xkey.state & ShiftMask)
1299                   offset = 1;
1300                 else
1301                   offset = 0;
1302                 
1303                 keySym = XLookupKeysym((XKeyEvent *)&event, offset);
1304                 if (keySym == XK_Escape)
1305                   {
1306                     XtUngrabKeyboard (shellWidget, CurrentTime);
1307                     XtUngrabPointer (shellWidget, CurrentTime);
1308                     *statusRet = DtHELP_SELECT_ABORT;
1309                     return(NULL);
1310                   }
1311             default:
1312                   XtDispatchEvent(&event);
1313       }
1314 #ifdef XTHREADS
1315         }    /* if */
1316 #endif
1317     } 
1318
1319     XtUngrabKeyboard (shellWidget, CurrentTime);      /* Done with keyboard */
1320     XtUngrabPointer (shellWidget, CurrentTime);      /* Done with pointer */
1321
1322     /* If its null then the user selected some area outside our window(s) */
1323     if (widget == shellWidget)
1324       {
1325         *statusRet = DtHELP_SELECT_INVALID;
1326         return (NULL);
1327       }
1328     if (!XtIsComposite (widget))
1329       {
1330         *statusRet = DtHELP_SELECT_VALID;
1331         return (widget);
1332       }
1333     
1334     /* Get the x and y and parent relative to the current window */
1335     parent = RootWindow(dpy, screen);
1336     target_win = XtWindow(widget);
1337     x = event.xbutton.x_root;
1338     y = event.xbutton.y_root;
1339
1340     XTranslateCoordinates(dpy, parent, target_win, x, y,
1341                              &new_x, &new_y, &sub);
1342     x = new_x;
1343     y = new_y;
1344
1345     comp_widget = (CompositeWidget)widget;
1346
1347     /*  look for gadgets at this point  */
1348     for (i = 0; i < comp_widget->composite.num_children; i++) {
1349         child = comp_widget->composite.children[i];
1350         /* put in check for only managed widgets here */
1351          if(XtIsManaged(child))
1352            if (PT_IN_CHILD (x, y, child))
1353               {
1354                 *statusRet = DtHELP_SELECT_VALID;
1355                 return (child);
1356               }
1357     }
1358
1359     *statusRet = DtHELP_SELECT_VALID;
1360     return (widget);
1361 }
1362
1363
1364
1365 \f
1366 /*****************************************************************************
1367  * Function:       Boolean RememberDir(String path)
1368  *
1369  * Parameters:          path            Specifies the path to check.
1370  *
1371  * Return Value:        Boolean         if the path name is good.
1372  *
1373  * Description: Use the directory caching mechanism to improve performance
1374  *              by remembering the directories that have already been
1375  *              stat'ed.
1376  *
1377  *****************************************************************************/
1378 static  Boolean
1379 RememberDir(String path)
1380 {
1381     int          result = 0;
1382     char        *ptr;
1383     struct stat  buf;
1384
1385     if (path == NULL || *path == '\0')
1386         return False;
1387
1388     if (_DtHelpCeStrrchr(path, "/", MB_CUR_MAX, &ptr) == 0 && ptr != path)
1389       {
1390         *ptr   = '\0';
1391         result = _DtHelpCeCheckAndCacheDir(path);
1392         *ptr   = '/';
1393       }
1394     if (result == 0 && access(path, R_OK) == 0 &&
1395                                 stat(path, &buf) == 0 && S_ISREG(buf.st_mode))
1396         return True;
1397
1398     return False;
1399 }
1400
1401 /*****************************************************************************
1402  * Function:       Boolean _DtHelpResolvePathname(
1403  *
1404  *
1405  * Parameters:     
1406  *
1407  * Return Value:    Boolean.
1408  *
1409  *
1410  * Description: _DtHelpResolvePathname attempts to validate and expand a path
1411  *              to a Cache Creek help access file.
1412  *
1413  *****************************************************************************/
1414 Boolean _DtHelpResolvePathname(
1415    Widget       widget,
1416    char * *     io_fileName,
1417    _DtHelpVolumeHdl * io_volumeHandle,
1418    char *       sysVolumeSearchPath,
1419    char *       userVolumeSearchPath)
1420 {
1421    String           newPath = NULL;
1422
1423    /* try to locate file and its entry, if present */
1424    newPath = _DtHelpFileLocate(DtHelpVOLUME_TYPE, *io_fileName,
1425                                   _DtHelpFileSuffixList,False,R_OK);
1426
1427    /* If we found a valid file let's do some set up here */
1428
1429    if (newPath != NULL)       /* We have a valid volume file so open it */
1430    {
1431        /* Close the current one if we have one open */
1432        if (*io_volumeHandle != NULL)
1433            _DtHelpCloseVolume(*io_volumeHandle);
1434
1435        /* Open the help volume file and save the handle id */
1436        if (_DtHelpOpenVolume(newPath,io_volumeHandle) >= 0)
1437        {
1438            /* Copy the expanded file location path */
1439            XtFree(*io_fileName);
1440            *io_fileName = newPath;
1441
1442            return(TRUE);
1443        }
1444        else
1445        {
1446            /* ERROR; leave io_fileName untouched on error
1447             * We used to set it to null here now we just return what came in
1448             */
1449            /* later NOTE: this seems strange, since we have closed the
1450               old volume, invalidating io_fileName */
1451            XtFree(newPath);
1452            XmeWarning(widget,(char*)UtilMessage2); 
1453            return (FALSE);
1454        }      
1455    }
1456    else                       /* We have a non-valid path */
1457    {
1458        /* ERROR; leave io_fileName untouched on error
1459         * We used to set it to null here now we just return what came in
1460         */
1461
1462        XmeWarning(widget,(char*)UtilMessage0);        
1463        return (FALSE);
1464    }
1465
1466 }
1467
1468
1469
1470
1471 \f
1472 /*****************************************************************************
1473  * Function:       Boolean _DtHelpExpandHelpVolume(DtHelpDialogWidget nw);
1474  *
1475  *
1476  * Parameters:     nw  Specifies the current help dialog widget.
1477  *
1478  * Return Value:    Boolean.
1479  *
1480
1481  * Description: _DtHelpExpandHelpVolume looks for a $LANG variable in the 
1482  *              helpAccesFile string and if found, replaces it with the 
1483  *              current lang variable.
1484  *
1485  *****************************************************************************/
1486 Boolean _DtHelpExpandHelpVolume(
1487    Widget                      w,
1488    _DtHelpDisplayWidgetStuff * display,
1489    _DtHelpCommonHelpStuff *    help,
1490    _DtHelpPrintStuff *         print)
1491 {
1492   Boolean validTopic = FALSE;
1493   Boolean validPath  = FALSE;
1494   char  *topLevelId;
1495
1496   /* Free the old, and Copy the new volumeHandle to printVolume varialbe */
1497   if (print->printVolume != NULL)
1498      XtFree(print->printVolume);
1499
1500   print->printVolume = XtNewString(display->helpVolume);     
1501
1502   validPath = _DtHelpResolvePathname((Widget)w,
1503                                 &print->printVolume,
1504                                 &display->volumeHandle,
1505                                 help->sysVolumeSearchPath,
1506                                 help->userVolumeSearchPath);
1507
1508  
1509   /* Check to see that we resolved our path correctly */
1510   if (!validPath)
1511     return (FALSE);                     /* RETURN */
1512   else
1513     {
1514
1515        /* The following routine will malloc memory for the topLevelId
1516         * variable, so we must free our current version first.
1517         */
1518        XtFree(help->topLevelId);
1519
1520        /* Assign our top level topic for this help access file */
1521        validTopic = _DtHelpCeGetTopTopicId(display->volumeHandle, &topLevelId);
1522
1523        if (!validTopic)
1524          {
1525            /* Bad top level topic */
1526            help->topLevelId = NULL;
1527            return(FALSE);
1528          }
1529        else   
1530          {
1531             /* recall that the topLevelId/File vars are malloc'd */
1532             help->topLevelId = topLevelId;
1533             return(TRUE); 
1534          }
1535     }
1536 }
1537
1538 \f
1539 /*****************************************************************************
1540  * Function:       char *_DtHelpParseIdString(char * specification);
1541  *
1542  *
1543  * Parameters:     specification  Specifies an author defined help topic.
1544  *
1545  * Return Value:    Void.
1546  *
1547  * Description:   This function copies the locationId portion of the 
1548  *                specification and returns it to the calling routine.
1549  *
1550  *****************************************************************************/
1551 char *_DtHelpParseIdString(
1552    char *specification)
1553 {
1554
1555   char *pAccessFile = NULL;
1556   char *tmpSpec=NULL;
1557   char *returnStr=NULL;
1558   char *strtok_ptr;
1559
1560   tmpSpec = XtNewString(specification);
1561
1562   
1563   /* First look for a blank in the specification.  This will signify that
1564    * we have a HelpAccessFile as part of the specification.
1565    */
1566   
1567   /* The first call will return true, with the first string */
1568   pAccessFile = DtStrtok_r(tmpSpec, " ", &strtok_ptr);
1569   returnStr = XtNewString(pAccessFile);
1570
1571   /* The second call will return true only if we have another string */
1572   pAccessFile = DtStrtok_r(NULL, " ", &strtok_ptr);
1573
1574   if (pAccessFile != NULL)
1575     {
1576       /* We have a helpAccessFile in our specification */
1577            
1578       XtFree(returnStr);
1579    
1580       returnStr = XtNewString(pAccessFile);
1581       XtFree(tmpSpec);
1582       return(returnStr);
1583
1584     }
1585   else
1586     {
1587       /* We don't have a helpAccessFile as part of the specificaiton
1588        * so we just return our locationId.
1589        */
1590        XtFree(tmpSpec);
1591        return (returnStr);
1592      }
1593
1594 }
1595
1596
1597 \f
1598 /*****************************************************************************
1599  * Function:       char *_DtHelpParseAccessFile(char * specification);
1600  *
1601  *
1602  * Parameters:     specification  Specifies an author defined help topic.
1603  *
1604  * Return Value:    Void.
1605  *
1606  * Description:   This function copies the helpAccessFile portion of the 
1607  *                specification and returns it to the calling routine.
1608  *
1609  *****************************************************************************/
1610 char *_DtHelpParseAccessFile(
1611    char *specification)
1612 {
1613   char *pAccessFile = NULL;
1614   char *tmpSpec=NULL;
1615   char *returnStr=NULL;
1616   char *strtok_ptr;
1617
1618   tmpSpec = XtNewString(specification);
1619
1620   
1621   /* First look for a blank in the specification.  This will signify that
1622    * we have a HelpAccessFile as part of the specification.
1623    */
1624   
1625   /* The first call will return true, with the first string */
1626   pAccessFile = DtStrtok_r(tmpSpec, " ", &strtok_ptr);
1627   returnStr = XtNewString(pAccessFile);
1628
1629   /* The second call will return true only if we have another string */
1630   pAccessFile = DtStrtok_r(NULL, " ", &strtok_ptr);
1631
1632   if (pAccessFile != NULL)
1633     {
1634       /* We have a helpAccessFile in our specification */
1635            
1636
1637       /* If we have an accessFile, but it's not a full path, then we
1638        * must get the full path from the reg file.
1639        */
1640       XtFree(tmpSpec);
1641       return(returnStr);
1642
1643     }
1644   else
1645     {
1646       /* We don't have a helpAccessFile as part of the specificaiton
1647        * so return NULL.
1648        */
1649        XtFree(returnStr);
1650        XtFree(tmpSpec);
1651        return (NULL);
1652      }
1653
1654 }
1655
1656
1657
1658
1659
1660
1661
1662
1663 /*****************************************************************************
1664  * Function:     DtHelpReturnSelectedWidgetId 
1665  *
1666  * Parameters:   parent      Specifies the widget ID to use as the bases of 
1667  *                           interaction, usually a top level shell.
1668  *
1669  *               cursor      Specifies the cursor to be used for the pointer
1670  *                           during the interaction.  If a value of NULL is 
1671  *                           used this function will use a default cursor
1672  *                           value.
1673  *
1674  *               widget      This is the return value (e.g. the selected 
1675  *                           widget).  A value of NULL is returned on error.
1676  *
1677  * Return Value:  Status: (-1,0 or 1).
1678  *
1679  * Purpose: Allows developers to get the widget ID for any widget in their UI
1680  *          that the user has selected via the pointer.  This function will
1681  *          cause the cursor to change and allow a user to select an item in 
1682  *          the UI.
1683  *
1684  *****************************************************************************/
1685 int DtHelpReturnSelectedWidgetId(
1686     Widget parent,
1687     Cursor cursor,
1688     Widget  *widget)
1689 {
1690
1691
1692  Display   *dpy;
1693  int       screen;  
1694  Widget    selectedItem;
1695  int       status=DtHELP_SELECT_ERROR;
1696  Screen    *retScr;
1697  int       result;
1698  _DtHelpWidgetToAppContext(parent);
1699
1700   _DtHelpAppLock(app);
1701   /* Setup some needed variables */
1702   dpy = XtDisplay(parent);
1703   retScr = XtScreen(parent);
1704  
1705   screen = XScreenNumberOfScreen(retScr);
1706  
1707   /* refresh the display */
1708   XmUpdateDisplay(parent);
1709
1710   /* Change the curser to let the user select the desired widget */
1711   selectedItem = LocateWidgetId(dpy, screen, &status, parent, cursor);
1712   
1713   switch (status)
1714     {
1715       case DtHELP_SELECT_VALID:
1716         *widget = selectedItem;
1717         result = DtHELP_SELECT_VALID;
1718         break;
1719      
1720       case DtHELP_SELECT_ABORT:
1721         *widget = NULL;
1722         result = DtHELP_SELECT_ABORT;
1723         break;
1724
1725       case DtHELP_SELECT_ERROR:
1726         *widget = NULL;
1727         result = DtHELP_SELECT_ERROR;
1728         break;
1729  
1730       case DtHELP_SELECT_INVALID:
1731       default:
1732         *widget = NULL;
1733         result = DtHELP_SELECT_INVALID;
1734         break;
1735     }
1736
1737   _DtHelpAppUnlock(app);
1738   return result;
1739 }
1740
1741
1742
1743
1744
1745
1746
1747 \f
1748 /*****************************************************************************
1749  * Function:        void _DtHelpTopicListAddToHead(
1750  *                                       char *locationId,
1751  *                                       int topicType,
1752  *                                       int maxNodex,
1753  *                                       DtTopicListStruct *pHead,
1754  *                                       DtTopicListStruct *pTale,
1755  *                                       totalNodes) 
1756  *                            
1757  *
1758  * Parameters:  
1759  *
1760  * Return Value:    Void.
1761  *
1762  * Purpose:         Adds an element to the top of the given topicList.
1763  *
1764  *****************************************************************************/
1765 void _DtHelpTopicListAddToHead(
1766     char *locationId,
1767     XmString topicTitle,
1768     int topicType,
1769     int maxNodes,
1770     char *accessPath,
1771     DtTopicListStruct **pHead,
1772     DtTopicListStruct **pTale,
1773     int *totalNodes,
1774     int scrollPosition)
1775 {
1776   DtTopicListStruct *pTemp=NULL;
1777
1778   /* add the new topic to the top */
1779   pTemp = (DtTopicListStruct *) XtMalloc((sizeof(DtTopicListStruct)));
1780
1781   pTemp->pNext            = (*pHead);
1782   pTemp->pPrevious        = NULL;
1783
1784   /* Assign the passed in values to our first element */
1785   pTemp->locationId       = XtNewString(locationId);
1786   pTemp->topicTitleLbl    = NULL;
1787   if (topicTitle != NULL)
1788       pTemp->topicTitleLbl= XmStringCopy(topicTitle);
1789   pTemp->topicType        = topicType;
1790   pTemp->helpVolume       = XtNewString(accessPath);
1791   pTemp->scrollPosition   = scrollPosition; 
1792
1793   /* Add locationId as first element if pHead = NULL */
1794   if (*pHead == NULL)
1795     {
1796       /* Assign our tale pointer */
1797       (*pTale) = (*pHead);
1798
1799       /* Make sure or totalNodes counter is correct, e.g. force it to 1 */
1800       *totalNodes = 1;
1801     }
1802   else 
1803     {  /* We have a list so add the new topic to the top */
1804     
1805      (*pHead)->pPrevious     = pTemp;
1806
1807      /* Assign our tale pointer only the first time in this block */
1808      if (*totalNodes == 1)
1809        (*pTale) = (*pHead);
1810
1811      /* Re-Assign our head pointer to point to the new head of the list */
1812
1813      /* Bump our totalNode count */
1814      *totalNodes = *totalNodes +1;
1815     }
1816
1817   /* set the head to the current entry */
1818   (*pHead) = pTemp;
1819
1820    /* If we have reached our maxNodes remove a node from the end of our list */
1821    if (*totalNodes > maxNodes)
1822      {
1823        pTemp            = (*pTale);
1824        (*pTale)         = (*pTale)->pPrevious;
1825        (*pTale)->pNext  = NULL;
1826        pTemp->pPrevious = NULL;
1827                
1828        /* Free the id String and AccessPath elements */
1829        XtFree(pTemp->locationId);
1830        XtFree(pTemp->helpVolume);
1831        if (pTemp->topicTitleLbl != NULL)
1832            XmStringFree(pTemp->topicTitleLbl);
1833
1834        /* Now, free the whole node */
1835        XtFree((char*)pTemp);
1836
1837        /* Bump back our total node counter */
1838        *totalNodes = *totalNodes -1;
1839
1840      }
1841 }
1842
1843
1844
1845 \f
1846 /*****************************************************************************
1847  * Function:        void _DtHelpTopicListDeleteHead(
1848  *                                       DtTopicListStruct *pHead,
1849  *                                       DtTopicListStruct *pTale,
1850  *                                       totalNodes) 
1851  *                            
1852  *
1853  * Parameters:  
1854  *
1855  * Return Value:    Void.
1856  *
1857  * Purpose:         Delets an element from the top of the given topicList.
1858  *
1859  *****************************************************************************/
1860 void _DtHelpTopicListDeleteHead(
1861     DtTopicListStruct **pHead,
1862     DtTopicListStruct **pTale,
1863     int *totalNodes)
1864 {
1865   DtTopicListStruct *pTemp=NULL;
1866
1867   /* Delete the top node in our topic list */
1868   if (*pHead != NULL)
1869     {
1870       pTemp = (*pHead);
1871       if(pTemp != (*pTale))        /* (e.g. more than one node in list) */
1872         {
1873            (*pHead)            = pTemp->pNext;
1874            pTemp->pNext        = NULL; 
1875            (*pHead)->pPrevious = NULL;
1876
1877            /* Free the id String and accessPath elements */
1878            XtFree(pTemp->locationId);
1879            XtFree(pTemp->helpVolume);
1880
1881            /* Now, free the whole node */
1882            XtFree((char*)pTemp);
1883
1884            /* Bump back our total node counter */
1885            *totalNodes = *totalNodes -1;
1886          } 
1887     }
1888 }
1889
1890
1891
1892
1893
1894 \f
1895 /*****************************************************************************
1896  * Function:        void _DtHelpMapCB()
1897  *                   
1898  *                            
1899  *
1900  * Parameters:      client_data is the widget in reference to
1901  *                  which widget w is placed
1902  *
1903  * Return Value:    Void.
1904  *
1905  * Purpose:         Determins where a new child dialog should be mapped in
1906  *                  relation to its parent.
1907  *
1908  * Algorithm:       1. attempt left or right placement with no overlap
1909  *                  2. if fails, attempt up or down placement with no overlap
1910  *                  3. if fails, determines location with least
1911  *                     amount of overlap, and places there.
1912  *
1913  *****************************************************************************/
1914 XtCallbackProc _DtHelpMapCB(
1915     Widget w,
1916     XtPointer client_data,
1917     XtPointer call_data )
1918 {
1919     Arg         args[2];
1920     Widget      parent;
1921     Position    centeredY, bestX, bestY, pX, pY; 
1922     Dimension   pHeight, myHeight, pWidth, myWidth;
1923     Dimension   maxX, maxY;
1924     int         rhsX, lhsX, topY, botY;   /* needs to be int, not Dimension */
1925     Display *   display;
1926     Screen *    screen;
1927     int         screenNumber;
1928
1929     parent = (Widget)client_data;
1930     display = XtDisplay(w);
1931     screen = XtScreen(w);
1932     screenNumber = XScreenNumberOfScreen(screen);
1933
1934     pX = XtX(parent);
1935     pY = XtY(parent);
1936     if (pX < 0) pX = 0;
1937     if (pY < 0) pY = 0;
1938     pHeight = XtHeight(parent);
1939     pWidth = XtWidth(parent);
1940     myHeight = XtHeight(w);
1941     myWidth = XtWidth(w);
1942     maxX = XDisplayWidth(display,screenNumber);
1943     maxY = XDisplayHeight(display,screenNumber);
1944
1945     /* algorithm 
1946      * 1. attempt left or right placement with no overlap
1947      * 2. if fails, attempt up or down placement with no overlap
1948      * 3. if fails, places on the right in the middle
1949      */
1950     
1951     /* first try left right placement */
1952     bestY = pY + pHeight/2 - myHeight/2;
1953     centeredY = bestY;
1954     rhsX = pX + pWidth;
1955     lhsX = pX - myWidth - 8;     /* 8: account for border */
1956     if ( ((int)(rhsX + myWidth)) < ((int) maxX) ) bestX = rhsX;
1957     else if ( lhsX > 0 ) bestX = lhsX;
1958     else
1959     {
1960         /* then try up down placement */
1961         bestX = pX + pWidth/2 - myWidth/2;
1962         botY = pY + pHeight;
1963         topY = pY - myHeight - 44;     /* 44: account for menu border */
1964         if ( ((int)(botY + myWidth)) < ((int) maxY) ) bestY = botY;
1965         else if ( topY > 0 ) bestY = topY;
1966         else
1967         {
1968             /* otherwise, center vertically and on the right */
1969             bestX = maxX - myWidth;
1970             bestY = centeredY;
1971         }
1972     }
1973
1974     XtSetArg(args[0], XmNx, bestX);
1975     XtSetArg(args[1], XmNy,  bestY);
1976     XtSetValues(w, args, 2);
1977
1978     return((XtCallbackProc) NULL);
1979
1980 }
1981
1982
1983
1984
1985
1986 \f
1987 /*****************************************************************************
1988  * Function:      void _DtHelpMapCenteredCB(
1989  *
1990  *
1991  *
1992  * Parameters:
1993  *
1994  * Return Value:    Void.
1995  *
1996  * Purpose:       Determins where the center of our help dialog is and sets
1997  *                  where new child dialog should be mapped such that its centered.
1998  *
1999  *****************************************************************************/
2000 XtCallbackProc _DtHelpMapCenteredCB(
2001     Widget w,
2002     XtPointer client_data,
2003     XtPointer call_data )
2004 {
2005     Arg         args[2];
2006     Widget      parent;
2007     Position newX, newY, pY, pX;
2008     Dimension pHeight, myHeight, pWidth, myWidth;
2009
2010     parent = (Widget)client_data;
2011
2012     pX = XtX(parent);
2013     pY = XtY(parent);
2014     pHeight = XtHeight(parent);
2015     pWidth = XtWidth(parent);
2016     myHeight = XtHeight(w);
2017     myWidth = XtWidth(w);
2018
2019
2020     newY = ((Position)(pHeight - myHeight) / 2) + pY;
2021     newX = ((Position)(pWidth - myWidth) / 2) + pX;
2022
2023     XtSetArg(args[0], XmNx, newX);
2024     XtSetArg(args[1], XmNy, newY);
2025     XtSetValues(w, args, 2);
2026
2027     return((XtCallbackProc) NULL);
2028
2029 }
2030
2031
2032
2033 \f
2034 /*****************************************************************************
2035  * Function:       void _DtHelpDisplayDefinitionBox(
2036  *                            Widget new,   
2037  *                            Widget definitionBox,
2038  *                            char * path,
2039  *                            char * locationId);
2040  *       
2041  * Parameters:   
2042  *
2043  * Return Value:    
2044  *
2045  * Purpose:        This routine will create and post the definition box.
2046  *                 (e.g. the Quick Help Dialog widget)
2047  *
2048  ****************************************************************************/
2049 void _DtHelpDisplayDefinitionBox(
2050     Widget parent,  
2051     Widget **definitionBox, 
2052     char * path,
2053     char * locationId)
2054 {
2055
2056   Arg  args[10];
2057   int    n;
2058   Widget printWidget, helpWidget, backWidget;
2059   XmString closeString;
2060   char *title;
2061
2062   /* get the title from the main help dialog and use it here for */
2063   n = 0;
2064   XtSetArg (args[n], XmNtitle, &(title));  ++n;
2065   XtGetValues (XtParent(parent), args, n);
2066
2067
2068   if (*definitionBox == NULL)
2069     {
2070
2071       /* Create the QuickHelpDialog widget to use as the definition box */
2072       closeString = XmStringCreateLocalized(((char *)_DTGETMESSAGE
2073                               (HUSET, 2,"Close")));
2074       n =0;
2075       XtSetArg (args[n], DtNhelpVolume, path);            n++; 
2076       XtSetArg (args[n], DtNhelpType, DtHELP_TYPE_TOPIC); n++; 
2077       XtSetArg (args[n], DtNlocationId, locationId);      n++;
2078       XtSetArg (args[n], DtNcloseLabelString, closeString);  n++;
2079       XtSetArg (args[n], XmNtitle, title);                n++;
2080       *definitionBox = 
2081            (Widget *)DtCreateHelpQuickDialog(parent, "definitionBox", 
2082                                                args, n);
2083       XmStringFree(closeString);
2084
2085       /* Catch the close callback so we can destroy the widget */
2086       XtAddCallback((Widget)*definitionBox, DtNcloseCallback,
2087                     CloseDefBoxCB, (XtPointer) NULL);
2088
2089
2090       /* We do not want a print button for now so we unmap it */     
2091       printWidget = DtHelpQuickDialogGetChild ((Widget)*definitionBox, 
2092                                         DtHELP_QUICK_PRINT_BUTTON);
2093       XtUnmanageChild (printWidget);
2094   
2095       /* We do not want a help button for now so we unmap it */     
2096       helpWidget = DtHelpQuickDialogGetChild ((Widget)*definitionBox, 
2097                                        DtHELP_QUICK_HELP_BUTTON);
2098       XtUnmanageChild (helpWidget);
2099
2100
2101       /* We do not want a BACK button for now so we unmap it */     
2102       backWidget = DtHelpQuickDialogGetChild ((Widget)*definitionBox, 
2103                                            DtHELP_QUICK_BACK_BUTTON);
2104       XtUnmanageChild (backWidget);
2105
2106
2107   
2108       /*  Adjust the decorations for the dialog shell of the dialog  */
2109       n = 0;
2110       XtSetArg(args[n], XmNmwmFunctions, MWM_FUNC_RESIZE | MWM_FUNC_MOVE); n++;
2111       XtSetArg (args[n], XmNmwmDecorations, 
2112                 MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_RESIZEH); n++;
2113       XtSetValues (XtParent(*definitionBox), args, n);
2114     
2115       /* Add the popup position callback to our history dialog */
2116       XtAddCallback (XtParent(*definitionBox), XmNpopupCallback,
2117                     (XtCallbackProc)_DtHelpMapCenteredCB, (XtPointer)XtParent(parent));
2118
2119
2120
2121     }
2122   else
2123     {
2124        /* We already have one so lets use it. */
2125
2126        /* Set the proper title */
2127        n = 0;
2128        XtSetArg (args[n], XmNtitle, title);  ++n;
2129        XtSetValues (XtParent(*definitionBox), args, n);
2130    
2131        /* Set the proper contents. */
2132        n = 0;
2133        XtSetArg (args[n], DtNhelpType, DtHELP_TYPE_TOPIC); n++; 
2134        XtSetArg (args[n], DtNhelpVolume, path);             n++; 
2135        XtSetArg (args[n], DtNlocationId, locationId);       n++;
2136        XtSetValues ((Widget)*definitionBox, args, n);
2137   
2138     }
2139  
2140
2141   /* Display the dialog */
2142   XtManageChild((Widget)*definitionBox);     
2143   XtMapWidget(XtParent((Widget)*definitionBox));
2144  
2145 }
2146
2147 \f
2148 /*****************************************************************************
2149  * Function:       static void CloseDefBoxCB(
2150  *                            Widget w,   
2151  *                            XtPointer client_data,
2152  *                            XtPointer call_data);
2153  *       
2154  * Parameters:   
2155  *
2156  * Return Value:    
2157  *
2158  * Purpose:        This routine closes and destroys the Definition Box
2159  *                 Dialog Widget that we create.
2160  *
2161  ****************************************************************************/
2162 static void  CloseDefBoxCB(
2163     Widget w,
2164     XtPointer client_data,
2165     XtPointer call_data )
2166 {
2167
2168    XtUnmanageChild(w);
2169
2170
2171 }
2172
2173
2174
2175
2176 \f
2177 /*****************************************************************************
2178  * Function:       void _DtHelpDisplayFormatError()
2179   *       
2180  * Parameters:   
2181  *
2182  * Return Value:    
2183  *
2184  * Purpose:        This routine generate and display the proper errror 
2185  *                 message to the display area as well as send the proper 
2186  *                 error to XmWarning() function.
2187  *
2188  ****************************************************************************/
2189 void _DtHelpDisplayFormatError(
2190     XtPointer displayArea,
2191     Widget widget,   
2192     char *userError,
2193     char *systemError)
2194 {
2195    XtPointer  topicHandle;
2196
2197    /* Set the string to the current help dialog */
2198    (void) _DtHelpFormatAsciiStringDynamic(displayArea, userError, &topicHandle);
2199          
2200    /* We ignor the status return here, because if we error out here we are
2201     * in big trouble because this is an error routine
2202     */
2203    
2204    _DtHelpDisplayAreaSetList (displayArea, topicHandle, FALSE, -1);
2205
2206    if (systemError != NULL)  
2207      XmeWarning((Widget)widget, systemError);
2208   
2209 }
2210
2211  
2212 \f
2213 /*****************************************************************************
2214  * Function:       void _DtHelpCommonHelpInit()
2215   *       
2216  * Parameters:   
2217  *
2218  * Return Value:    
2219  *
2220  * Purpose:        This routine inits common help stuff
2221  *
2222  ****************************************************************************/
2223 void _DtHelpCommonHelpInit(
2224     _DtHelpCommonHelpStuff * help)
2225 {
2226    help->topLevelId          = NULL;
2227    help->currentHelpFile     = NULL;
2228
2229    /* for help on help */
2230    if ( help->helpOnHelpVolume != _DtHelpDefaultHelp4HelpVolume)
2231        help->helpOnHelpVolume = XtNewString(help->helpOnHelpVolume);
2232    if ( NULL == help->helpOnHelpVolume ) 
2233        help->helpOnHelpVolume = (char *)_DtHelpDefaultHelp4HelpVolume;
2234    help->pHelpListHead = NULL;          /* Help List Pointer */
2235    help->onHelpDialog = NULL;           /* help on help dialog */
2236    help->pDisplayArea = NULL;           /* Display widget handle */
2237
2238    /* get the search paths used by the widget */
2239    help->userVolumeSearchPath = _DtHelpGetUserSearchPath();
2240    help->sysVolumeSearchPath = _DtHelpGetSystemSearchPath();
2241 }
2242
2243  
2244 \f
2245 /*****************************************************************************
2246  * Function:       void _DtHelpCommonHelpClean()
2247   *       
2248  * Parameters:   
2249  *
2250  * Return Value:    
2251  *
2252  * Purpose:        This routine cleans up common help stuff
2253  *
2254  ****************************************************************************/
2255 void _DtHelpCommonHelpClean(
2256     _DtHelpCommonHelpStuff * help,
2257     Boolean                 destroy)
2258 {
2259    free(help->topLevelId);
2260    XtFree(help->currentHelpFile);
2261
2262    help->topLevelId          = NULL;
2263    help->currentHelpFile     = NULL;
2264
2265    if (destroy)
2266    {
2267       if (help->helpOnHelpVolume != _DtHelpDefaultHelp4HelpVolume)
2268          XtFree(help->helpOnHelpVolume);
2269
2270       /* Free all the info we saved for our help callbacks */
2271       _DtHelpListFree(&help->pHelpListHead);
2272
2273       XtFree(help->userVolumeSearchPath);
2274       XtFree(help->sysVolumeSearchPath);
2275
2276       memset(help,0,sizeof(_DtHelpCommonHelpStuff));
2277    }
2278    else
2279    {
2280       /* Set our display area to a null starting vlaues */
2281       _DtHelpDisplayAreaClean(help->pDisplayArea);
2282    }
2283 }
2284
2285  
2286 \f
2287 /*****************************************************************************
2288  * Function:       void _DtHelpSetDlgButtonsWidth
2289  *       
2290  * Parameters:   
2291  *
2292  * Return Value:    
2293  *
2294  * Purpose:        This routine cleans up common help stuff
2295  *
2296  ****************************************************************************/
2297 void _DtHelpSetButtonPositions(
2298     Widget     btnList[],
2299     int        numBtns,
2300     Dimension  minFormWidth,
2301     Dimension  btnMargins,
2302     Dimension  minBetweenBtnSpace)
2303 { /* position buttons */
2304
2305       /* This code adds up the sizes of the buttons to go into
2306          the bottom row and calculates the form position percentages.
2307          All buttons are the same size, and are able to hold all
2308          the strings that may be dynamically placed in them.
2309          All buttons are 5% apart. */
2310       /* This code is specifically written to handle 3 buttons
2311          and assumes that the first 3 strings are to the ActionBtn */
2312    Dimension minWidthWithSpace = 0;
2313    Dimension borderWidth = 0;
2314    Dimension sumWidth = 0;
2315    Dimension leftPos = 0;
2316    Dimension rightPos = 0;
2317    Dimension spaceWidth = 0;
2318    Dimension btnWidth;
2319    Dimension maxBtnWidth = 0;
2320    float scale = 0.0;
2321    XmFontList fontList = NULL;
2322    int        i;
2323    int        n;
2324    Arg        args[5];
2325
2326    if (numBtns <= 0 || NULL == btnList[0]) return;
2327
2328    /* get the fontList for the button */
2329    XtSetArg (args[0], XmNborderWidth, &borderWidth);
2330    XtSetArg (args[1], XmNfontList, &fontList);
2331    XtGetValues (btnList[0], args, 2);
2332    /* assumption: the fontList that is returned is not owned by me; don't free */
2333
2334    /* cycle through all buttons */
2335    for (i=0; i<numBtns && NULL!=btnList[i]; i++)
2336    {
2337       XmString labelString;
2338
2339       /* get the label from the button */
2340       XtSetArg (args[0], XmNlabelString, &labelString);
2341       XtGetValues (btnList[i], args, 1);
2342
2343       btnWidth = XmStringWidth(fontList,labelString) + borderWidth + btnMargins;
2344       if (btnWidth > maxBtnWidth) maxBtnWidth = btnWidth;
2345
2346       XmStringFree(labelString);
2347    } /* for calcing widths */
2348    numBtns = i;  /* number of valid buttons */
2349
2350    /* calc the space */
2351    sumWidth = maxBtnWidth * numBtns;
2352    minWidthWithSpace = sumWidth + minBetweenBtnSpace * (numBtns * 2);
2353    if (minWidthWithSpace > minWidthWithSpace) minFormWidth = minWidthWithSpace;
2354    spaceWidth = ((int)(minFormWidth - sumWidth) / (numBtns * 2));
2355
2356    /* scale pixels to percent */
2357    scale = 100.0 / (float) minFormWidth;
2358
2359    /* set the positions of each button */
2360    leftPos = spaceWidth;
2361    for ( i=0; i<numBtns; i++ )
2362    {
2363       rightPos = leftPos + maxBtnWidth;
2364    
2365       n = 0;
2366       XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION);            n++;
2367       XtSetArg (args[n], XmNleftPosition, (Dimension) (((float) leftPos)*scale)); n++;
2368       XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION);           n++;
2369       XtSetArg (args[n], XmNrightPosition,(Dimension) (((float) rightPos)*scale)); n++;
2370       XtSetValues (btnList[i], args, n);
2371    
2372       leftPos = rightPos + spaceWidth + spaceWidth;
2373    }  /* setup the positions for all buttons */
2374 }  /* _DtHelpSetDlgButtonsWidth */
2375
2376
2377 \f
2378 /*****************************************************************************
2379  * Function:       _DtHelpXmFontListGetPropertyMax
2380  *       
2381  * Parameters:   
2382  *     fontList:  an XmFontList
2383  *     atom:      an XA_xxx value (see Vol 1, chpt 6.2.9)
2384  *     ret_propertyValue: ptr to long value that will hold the max value
2385  *
2386  * Return Value:    
2387  *     True: got at least one value
2388  *     False: unable to get any value; ret_propertyValue unchanged
2389  *
2390  * Purpose:
2391  *    This function returns the max value of XGetFontProperty calls 
2392  *    for each font in the XmFontList
2393  *    If there is an error, ret_propertyValue is left unchanged.
2394  *
2395  ****************************************************************************/
2396 Boolean
2397 _DtHelpXmFontListGetPropertyMax(
2398         XmFontList fontList,
2399         Atom atom,
2400         unsigned long *ret_propertyValue)
2401 {
2402     Boolean gotValue = False;
2403     XmFontContext context;
2404     XmFontListEntry entry;
2405     XtPointer fontData;
2406
2407     if (NULL == fontList) return False;           /* RETURN on error */
2408
2409     /* walk through the fontList entries and add them in */
2410     XmFontListInitFontContext(&context,fontList);
2411     for ( entry = XmFontListNextEntry(context);
2412           NULL != entry;
2413           entry = XmFontListNextEntry(context) )
2414     {
2415        unsigned long value;
2416        XmFontType type;
2417
2418        fontData = XmFontListEntryGetFont(entry,&type);
2419        if (XmFONT_IS_FONT == type)
2420        {
2421           XFontStruct * fontStruct;
2422
2423           /* cast according to type */
2424           fontStruct = (XFontStruct *) fontData;
2425
2426           if(XGetFontProperty(fontStruct, atom, &value) == TRUE) 
2427           {
2428              if(gotValue == False) /* haven't gotten any prop value yet */
2429              {
2430                  *ret_propertyValue = value;
2431                  gotValue = True;
2432              }
2433              else   /* have a good prop value already...get the max one */
2434              {
2435                  if(value > *ret_propertyValue)
2436                      *ret_propertyValue = value;
2437              }
2438           } /* if the getproperty call was good */
2439        }
2440        else  /* XmFONT_IS_FONTSET */
2441        {
2442           XFontSet fontSet;
2443           XFontStruct **font_list;
2444           char **name_list;
2445           int numfont;
2446           int i;
2447
2448           /* cast according to type */
2449           fontSet = (XFontSet) fontData;
2450  
2451           numfont=XFontsOfFontSet(fontSet,&font_list,&name_list);
2452           for(i = 0; i < numfont; i++) 
2453           {
2454               if(XGetFontProperty(font_list[i], atom, &value) == TRUE) 
2455               {
2456                   if(gotValue == False) /* haven't gotten any prop value yet */
2457                   {
2458                       *ret_propertyValue = value;
2459                       gotValue = True;
2460                   }
2461                   else   /* have a good prop value already...get the max one */
2462                   {
2463                       if(value > *ret_propertyValue)
2464                           *ret_propertyValue = value;
2465                   }
2466               } /* if the getproperty call was good */
2467           } /* for all fonts in the font set */
2468        } /* this entry uses a font set */
2469     } /* for all font entries in the font list */
2470     XmFontListFreeFontContext(context);
2471
2472     return(gotValue);
2473 }