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