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