7e0a9517e3
* 64bit warnings * Missing prototypes * explicit parentheses / brackets There's still some work to be done, but, as always, it's a start ...
2476 lines
77 KiB
C
2476 lines
77 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these librararies and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $TOG: HelpUtil.c /main/19 1998/04/09 17:43:30 mgreess $ */
|
|
/************************************<+>*************************************
|
|
****************************************************************************
|
|
**
|
|
** File: HelpUtil.c
|
|
**
|
|
** Project: Rivers Project
|
|
**
|
|
** Description:
|
|
**
|
|
**
|
|
** (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
|
|
**
|
|
** (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
** (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
** (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
** (c) Copyright 1993, 1994 Novell, Inc.
|
|
**
|
|
****************************************************************************
|
|
************************************<+>*************************************/
|
|
|
|
|
|
#include <sys/param.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h> /* R_OK */
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
#define X_INCLUDE_PWD_H
|
|
#define XOS_USE_XT_LOCKING
|
|
#include <X11/Xos_r.h>
|
|
|
|
#include <Xm/Xm.h>
|
|
#include <Xm/XmP.h>
|
|
#include <Xm/MwmUtil.h>
|
|
|
|
#include <Xm/MessageB.h>
|
|
#include <X11/Shell.h>
|
|
#include <X11/Intrinsic.h>
|
|
#include <X11/cursorfont.h>
|
|
|
|
/* private includes */
|
|
#include "Access.h"
|
|
#include "bufioI.h"
|
|
#include "DisplayAreaI.h"
|
|
#include "DisplayAreaP.h"
|
|
|
|
#include <Dt/Help.h>
|
|
#include "HelpI.h"
|
|
#include "HelpP.h"
|
|
#include "StringFuncsI.h"
|
|
#include "HelpDialogI.h"
|
|
#include "HelpDialogP.h"
|
|
#include "HelpUtilI.h"
|
|
#include "HelposI.h"
|
|
#include "HyperTextI.h"
|
|
#include "FormatI.h"
|
|
#include "MessagesP.h"
|
|
#include "HelpQuickD.h"
|
|
#include "SetListI.h"
|
|
#include "DestroyI.h"
|
|
#include "HelpAccessI.h"
|
|
#include "FileUtilsI.h"
|
|
#include "HourGlassI.h"
|
|
#include "Lock.h"
|
|
|
|
#include <Dt/DtNlUtils.h>
|
|
|
|
/******* global variables *******/
|
|
char _DtHelpDefaultHelp4HelpVolume[] = "Help4Help";
|
|
char _DtHelpDefaultLocationId[] = "_HOMETOPIC";
|
|
|
|
/**** Help Util Error message Defines ****/
|
|
|
|
#define UtilMessage0 _DtHelpMsg_0010
|
|
#define UtilMessage2 _DtHelpMsg_0011
|
|
|
|
|
|
static void _DtMessageClose(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XEvent *event);
|
|
static void CloseDefBoxCB(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data);
|
|
|
|
/* Macro for finding a point within a gadget.
|
|
* Its used for item help
|
|
*/
|
|
#define PT_IN_CHILD(X, Y, CHILD) \
|
|
((((int)(X)) >= ((int) (CHILD)->core.x)) && \
|
|
(((int)(X)) <= ((int)((CHILD)->core.x + (CHILD)->core.width))) && \
|
|
(((int)(Y)) >= ((int) (CHILD)->core.y)) && \
|
|
(((int)(Y)) <= ((int)((CHILD)->core.y + (CHILD)->core.height))))
|
|
|
|
|
|
/******** useful constants ********/
|
|
#define EOS '\0'
|
|
#define DIR_SLASH '/'
|
|
|
|
#define HUSET 8 /* message catalog set */
|
|
|
|
/******** static variables ********/
|
|
|
|
|
|
/******** data structures ********/
|
|
typedef struct ExecContext
|
|
{
|
|
char * command;
|
|
XtPointer pDisplayArea;
|
|
} ExecContext;
|
|
|
|
/******** The onitem cursor (32x32, xbm format) ********/
|
|
#define onitem32_width 32
|
|
#define onitem32_height 32
|
|
#define onitem32_x_hot 0
|
|
#define onitem32_y_hot 0
|
|
static unsigned char onitem32_bits[] = {
|
|
0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x1f, 0xfc, 0xf9, 0xff, 0xe7, 0xf3,
|
|
0xf1, 0xff, 0xfb, 0xef, 0xe1, 0xff, 0xfd, 0xdf, 0xc1, 0xff, 0xfd, 0xdf,
|
|
0x83, 0xff, 0xfe, 0xbf, 0x03, 0xff, 0x7e, 0x7e, 0x03, 0xfe, 0xbe, 0x7d,
|
|
0x03, 0xfc, 0xbe, 0x7d, 0x03, 0xf0, 0xc1, 0x7d, 0x03, 0xe0, 0xff, 0x7e,
|
|
0x07, 0xc0, 0x7f, 0xbf, 0x07, 0x80, 0xbf, 0xbf, 0x07, 0x00, 0xde, 0xdf,
|
|
0x07, 0x00, 0xdc, 0xef, 0x07, 0x00, 0xdf, 0xf7, 0x07, 0x80, 0xdf, 0xfb,
|
|
0x0f, 0xc0, 0xdf, 0xfb, 0x0f, 0xc0, 0xdf, 0xfb, 0x0f, 0x81, 0xdf, 0xfb,
|
|
0xcf, 0x83, 0x3f, 0xfc, 0xef, 0x07, 0xff, 0xff, 0xff, 0x07, 0xff, 0xff,
|
|
0xff, 0x0f, 0x3e, 0xfc, 0xff, 0x0f, 0xde, 0xfb, 0xff, 0x1f, 0xdc, 0xfb,
|
|
0xff, 0x1f, 0xdc, 0xfb, 0xff, 0x3f, 0xd8, 0xfb, 0xff, 0x3f, 0x3c, 0xfc,
|
|
0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
|
|
#define onitem32_m_width 32
|
|
#define onitem32_m_height 32
|
|
#define onitem32_m_x_hot 0
|
|
#define onitem32_m_y_hot 0
|
|
static unsigned char onitem32_m_bits[] = {
|
|
0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0xe0, 0x03, 0x0f, 0x00, 0xf8, 0x0f,
|
|
0x1f, 0x00, 0xfc, 0x1f, 0x3f, 0x00, 0xfe, 0x3f, 0x7f, 0x00, 0xfe, 0x3f,
|
|
0xfe, 0x00, 0xff, 0x7f, 0xfe, 0x01, 0xff, 0xff, 0xfe, 0x03, 0x7f, 0xfe,
|
|
0xfe, 0x0f, 0x7f, 0xfe, 0xfe, 0x1f, 0x3e, 0xfe, 0xfe, 0x3f, 0x00, 0xff,
|
|
0xfc, 0x7f, 0x80, 0x7f, 0xfc, 0xff, 0xc1, 0x7f, 0xfc, 0xff, 0xe3, 0x3f,
|
|
0xfc, 0xff, 0xe7, 0x1f, 0xfc, 0xff, 0xe3, 0x0f, 0xfc, 0xff, 0xe0, 0x07,
|
|
0xf8, 0x7f, 0xe0, 0x07, 0xf8, 0x7f, 0xe0, 0x07, 0xf8, 0xff, 0xe0, 0x07,
|
|
0xf8, 0xfe, 0xc0, 0x03, 0x38, 0xfc, 0x01, 0x00, 0x18, 0xfc, 0x01, 0x00,
|
|
0x00, 0xf8, 0xc3, 0x03, 0x00, 0xf8, 0xe3, 0x07, 0x00, 0xf0, 0xe7, 0x07,
|
|
0x00, 0xf0, 0xe7, 0x07, 0x00, 0xe0, 0xef, 0x07, 0x00, 0xe0, 0xc7, 0x03,
|
|
0x00, 0xc0, 0x03, 0x00, 0x00, 0x80, 0x00, 0x00};
|
|
|
|
#if 0 /* XPM format */
|
|
static char * onitem32_xpm[] = {
|
|
/* width height ncolors cpp [x_hot y_hot] */
|
|
"32 32 3 1 0 0",
|
|
/* colors */
|
|
" s iconColor1 m black c black",
|
|
". s background m black c #969696969696",
|
|
"X s iconColor2 m white c white",
|
|
/* pixels */
|
|
" ..............................",
|
|
" X ..................XXXXX......",
|
|
" XX ...............XX XX....",
|
|
" XXX .............X X...",
|
|
" XXXX ...........X X..",
|
|
" XXXXX ..........X X..",
|
|
". XXXXX ........X X.",
|
|
". XXXXXX .......X XX X",
|
|
". XXXXXXX ......X X..X X",
|
|
". XXXXXXXX ....X X..X X",
|
|
". XXXXXXXXXX ....XXXXX...X X",
|
|
". XXXXXXXXXXX ..........X X",
|
|
".. XXXXXXXXXXX ........X X.",
|
|
".. XXXXXXXXXXXX .....X X.",
|
|
".. XXXXXXXXXXXXXX ...X X..",
|
|
".. XXXXXXXXXXXXXXX ..X X...",
|
|
".. XXXXXXXXXXXXX ...X X....",
|
|
".. XXXXXXXXXXXX .....X X.....",
|
|
"... XXXXXXXXXX ......X X.....",
|
|
"... XXXXXXXXXX ......X X.....",
|
|
"... XXXX XXXXXX .....X X.....",
|
|
"... XX . XXXXX ......XXXX......",
|
|
"... X .... XXXXX ...............",
|
|
"... ..... XXXXX ...............",
|
|
"........... XXXXX ....XXXX......",
|
|
"........... XXXXX ...X X.....",
|
|
"............ XXXXX ..X X.....",
|
|
"............ XXXXX ..X X.....",
|
|
"............. XXXXX .X X.....",
|
|
"............. XXXX ...XXXX......",
|
|
".............. X ..............",
|
|
"............... ................"};
|
|
#endif
|
|
|
|
#define ROOT_USER 0
|
|
#define BIN_USER 2
|
|
#define SYS_USER 3
|
|
|
|
#define NO_CONDITION 0
|
|
#define MISMATCHING_HOME_DIRS 1
|
|
|
|
/* ------------------------------------------------------------ *
|
|
**
|
|
** Function trusted
|
|
**
|
|
** Purpose Determines if the passed help volume is a
|
|
** "trusted" help volume or not. We call it
|
|
** trusted if it meets the following conditions:
|
|
** 1. File Owner is root, bin, or system.
|
|
** 2. File is NOT writable by group or others.
|
|
**
|
|
** Returns
|
|
** True - if the help volume IS Trusted
|
|
** False - if the help volume is NOT Trusted
|
|
**
|
|
** ------------------------------------------------------------ */
|
|
static Boolean trusted (char *hv_path) /* Full path to the help volume */
|
|
{
|
|
struct stat buf;
|
|
Boolean writable;
|
|
|
|
if ( (stat (hv_path, &buf)) == -1)
|
|
return False;
|
|
|
|
|
|
/* S_IWGRP */ /* write group */
|
|
/* S_IWOTH */ /* write other */
|
|
|
|
/** ---------------------------------------------------------------------- *
|
|
** The help volume MUST be owned by root, bin, or sys to be trusted.
|
|
** ---------------------------------------------------------------------- */
|
|
|
|
if ( buf.st_uid != ROOT_USER &&
|
|
buf.st_uid != BIN_USER &&
|
|
buf.st_uid != SYS_USER)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
/** ----------------------------------------------------------------------- *
|
|
** The help volume MUST not be writable by group or others to be trusted.
|
|
** ----------------------------------------------------------------------- */
|
|
|
|
writable = (((buf.st_mode & S_IWGRP) == 0) && (buf.st_mode & S_IWOTH) == 0) ?
|
|
True : False;
|
|
|
|
return writable;
|
|
|
|
}
|
|
|
|
|
|
/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
|
/**** API Error Dialog Support Functions *****/
|
|
/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
|
|
|
|
|
|
/************************************************************************
|
|
* Function: _DtMessageClose
|
|
*
|
|
* Close the error/info message box.
|
|
*
|
|
************************************************************************/
|
|
static void _DtMessageClose(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XEvent *event )
|
|
{
|
|
/* NOTE: ExecuteContextCB() is dependent on this code */
|
|
if (event->type == UnmapNotify)
|
|
{
|
|
XtRemoveEventHandler (XtParent (client_data), StructureNotifyMask,
|
|
True, (XtEventHandler) _DtMessageClose, client_data);
|
|
|
|
XtUnmanageChild (client_data);
|
|
XtDestroyWidget (client_data);
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* Function: ExecuteContextCB
|
|
*
|
|
* Execute an execution context
|
|
*
|
|
************************************************************************/
|
|
static void ExecuteContextCB(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XtPointer callData )
|
|
{
|
|
ExecContext * ec = (ExecContext *) client_data;
|
|
|
|
if (ec && ec->command && ec->pDisplayArea)
|
|
{
|
|
_DtHelpExecProcedure(ec->pDisplayArea,ec->command);
|
|
free(ec->command);
|
|
}
|
|
XtFree((char *) ec);
|
|
|
|
/* unmap, rather than unmanage and destroy, because of the code
|
|
in _DtMessageClose(). _DtMessageClose() is notified when
|
|
the widget unmaps and it destroys the widget. */
|
|
XtUnmapWidget(w); /* w is the message dialog */
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Function: CreateErrorDialog
|
|
*
|
|
* Creates an XmMessageDialog with the message and all buttons
|
|
* except the 'Close' (OK) button unmanaged.
|
|
* Also adds a callback that destroys the widget when the dialog is closed.
|
|
*
|
|
*****************************************************************************/
|
|
static Widget
|
|
CreateErrorDialog(
|
|
Widget parent,
|
|
char * message)
|
|
{
|
|
Widget button;
|
|
Widget messageDialog;
|
|
Arg args[10];
|
|
int n;
|
|
|
|
XmString label_string;
|
|
XmString ok_string;
|
|
char *title_string;
|
|
|
|
/* Setup the message string and dialog title */
|
|
|
|
ok_string = XmStringCreateLocalized(((char *)_DTGETMESSAGE
|
|
(HUSET, 2,"Close")));
|
|
label_string = XmStringCreateLocalized(message);
|
|
title_string = XtNewString((char *)_DTGETMESSAGE
|
|
(HUSET, 5,"Help Error"));
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNmessageString, label_string); n++;
|
|
XtSetArg (args[n], XmNtitle,title_string); n++;
|
|
XtSetArg (args[n], XmNcancelLabelString, ok_string); n++;
|
|
XtSetArg (args[n], XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON); n++;
|
|
messageDialog = XmCreateErrorDialog (parent, "errorDialog",
|
|
args, n);
|
|
XtSetArg(args[0], XmNmwmDecorations,
|
|
MWM_DECOR_BORDER | MWM_DECOR_TITLE);
|
|
XtSetArg(args[1], XmNuseAsyncGeometry, True);
|
|
XtSetValues(XtParent(messageDialog), args, 2);
|
|
|
|
XmStringFree (label_string);
|
|
XmStringFree (ok_string);
|
|
XtFree(title_string);
|
|
|
|
/* unmanage or define the other buttons */
|
|
button = XmMessageBoxGetChild (messageDialog, XmDIALOG_OK_BUTTON);
|
|
XtUnmanageChild (button);
|
|
button = XmMessageBoxGetChild (messageDialog, XmDIALOG_HELP_BUTTON);
|
|
XtUnmanageChild (button);
|
|
|
|
/* StructureNotifyMask gets Circulate, Configure, Destroy,
|
|
Gravity, Map, Reparent, & Unmap events */
|
|
XtAddEventHandler(XtParent(messageDialog),
|
|
StructureNotifyMask,True,
|
|
(XtEventHandler) _DtMessageClose, (XtPointer) messageDialog);
|
|
|
|
return messageDialog; /* RETURN */
|
|
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Function: CreateExecErrorDlg
|
|
*
|
|
*
|
|
*
|
|
* Called by:
|
|
*****************************************************************************/
|
|
static Widget
|
|
CreateExecErrorDlg(
|
|
Widget helpWidget,
|
|
const char * cmdStr,
|
|
Boolean invalidAlias,
|
|
_DtHelpCommonHelpStuff * pHelpStuff,
|
|
int condition,
|
|
char * current_hd)
|
|
{
|
|
DtHelpListStruct *pHelpInfo;
|
|
Widget msgDlg;
|
|
Widget btn;
|
|
char * msg;
|
|
char * fullmsg;
|
|
|
|
/* handle the error case */
|
|
if (invalidAlias)
|
|
{
|
|
msg = (char *)_DTGETMESSAGE(HUSET, 12,
|
|
"The help volume wanted to execute a command alias.\n"
|
|
"The alias '%s' is not defined.");
|
|
}
|
|
|
|
else if (condition == MISMATCHING_HOME_DIRS)
|
|
{
|
|
msg = (char *)_DTGETMESSAGE(HUSET, 14,
|
|
"The help volume wanted to execute a command as the root user, but the\n"
|
|
"home directory of \"%s\" ($HOME) does not match the root\n"
|
|
"user's home directory. This could result in executing unexpected\n"
|
|
"commands.\n\n"
|
|
|
|
"The command is: \"%s\"\n\n"
|
|
|
|
"Note: to avoid this in the future:\n"
|
|
" execute \"su - root\" rather than \"su root\".\n");
|
|
}
|
|
|
|
else
|
|
{
|
|
msg = (char *)_DTGETMESSAGE(HUSET, 13,
|
|
"The help volume wanted to execute a command.\n"
|
|
"For security reasons, automatic command execution is turned off.\n"
|
|
"The command is: %s");
|
|
}
|
|
fullmsg = (char *) malloc(strlen(msg)+strlen(cmdStr)+30);
|
|
if (fullmsg)
|
|
{
|
|
if (condition == MISMATCHING_HOME_DIRS)
|
|
sprintf(fullmsg, msg, current_hd, cmdStr);
|
|
else
|
|
sprintf(fullmsg,msg,cmdStr);
|
|
}
|
|
else
|
|
fullmsg = msg;
|
|
|
|
/* create an error dialog, but don't manage it yet */
|
|
msgDlg = CreateErrorDialog(XtParent(helpWidget),fullmsg);
|
|
|
|
if (msg != fullmsg) free(fullmsg);
|
|
|
|
btn = XmMessageBoxGetChild (msgDlg, XmDIALOG_HELP_BUTTON);
|
|
XtManageChild (btn); /* re-manage the button */
|
|
|
|
/* add the HelpOnHelp callback */
|
|
pHelpInfo = _DtHelpListAdd(DtHELP_ExecutionPolicy_STR,
|
|
helpWidget, pHelpStuff, &pHelpStuff->pHelpListHead);
|
|
XtAddCallback(btn, XmNactivateCallback, _DtHelpCB, (XtPointer) pHelpInfo);
|
|
|
|
return msgDlg;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Function: _DtHelpErrorDialog
|
|
*
|
|
*
|
|
*
|
|
* Called by:
|
|
*****************************************************************************/
|
|
void _DtHelpErrorDialog(
|
|
Widget parent,
|
|
char * message)
|
|
{
|
|
Widget messageDialog;
|
|
|
|
messageDialog = CreateErrorDialog(parent,message);
|
|
|
|
/* Display help window. This used to be before the call
|
|
to add a StructureNotify event handler */
|
|
XtManageChild (messageDialog);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: _DtHelpFilterExecCmdStr
|
|
*
|
|
* Args:
|
|
* helpWidget: help widget requesting to exec the command
|
|
* pDisplayStuff: ptr to the DisplayWidget stuff of the help widget
|
|
* commandStr: command string to execute
|
|
* ret_cmdStr: the screened & possibly rewritten command is put here
|
|
* ret_invalidAlias: was the command an invalid alias?
|
|
* ret_execPermitted: if executionPolicy permit exec & ret_cmdStr is valid
|
|
* ret_queryNeeded: if executionPolicy requires a query before exec
|
|
*
|
|
* Description:
|
|
* ret_cmdStr gets memory owned by the calling function; it should be
|
|
* freed when no longer needed. The string will be the same as the
|
|
* commandStr if commandStr was not an alias. If the commandStr
|
|
* is an alias and if the alias is defined, the ret_cmdStr will be the
|
|
* value of the alias. If the alias isn't defined, the ret_cmdStr will
|
|
* be the default command if available, or the alias name otherwise.
|
|
*
|
|
* ret_invalidAlias will be True if the alias was undefined and
|
|
* no default command was given.
|
|
*
|
|
* ret_execPermitted will be True if executionPolicy is DtHELP_EXECUTE_ALL
|
|
* or DtHELP_EXECUTE_QUERY_xxx and ret_cmdStr is valid
|
|
*
|
|
* ret_queryNeeded will be True if executionPoilcy is
|
|
* DtHELP_EXECUTE_QUERY_ALL or if it is DtHELP_EXECUTE_QUERY_UNALIASED
|
|
* and ret_cmdStr did not derive from an alias (i.e. was hardcoded
|
|
* in the help volume, not retrieved from a resource).
|
|
*
|
|
* Returns:
|
|
* True: if execPermitted and a valid command string
|
|
* False: if if execPermitted is False or invalid command string
|
|
*
|
|
* Comments:
|
|
* This code is written such that we don't need nor want to know
|
|
* whether it is a general or quick help widget.
|
|
*
|
|
* Warning:
|
|
* command string must be writable; it is written, but left
|
|
* unchanged when the function exits.
|
|
*
|
|
*****************************************************************************/
|
|
Boolean _DtHelpFilterExecCmdStr(
|
|
Widget helpWidget,
|
|
unsigned char executionPolicy,
|
|
const char * commandStr,
|
|
char * * ret_cmdStr,
|
|
Boolean * ret_invalidAlias,
|
|
Boolean * ret_execPermitted,
|
|
Boolean * ret_queryNeeded,
|
|
char * hv_path) /* Path to the Help Volume */
|
|
{
|
|
char * token;
|
|
char * tokenEnd;
|
|
char tokenEndChar;
|
|
char * aliasCommand = NULL;
|
|
Boolean ret;
|
|
|
|
#define RN_execAlias "executionAlias"
|
|
#define RC_execAlias "ExecutionAlias"
|
|
#define ExecAliasCmd "DtHelpExecAlias"
|
|
|
|
/* default values */
|
|
*ret_cmdStr = NULL;
|
|
*ret_invalidAlias = False;
|
|
*ret_execPermitted = False;
|
|
*ret_queryNeeded = False;
|
|
|
|
if (NULL == commandStr)
|
|
return False;
|
|
|
|
/** ------------------------------------------------------------- *
|
|
** If the executionPolicy is query all unaliased (query for all
|
|
** execution links that have no execution alias defined), we
|
|
** make an exception: only query the user for help volumes
|
|
** deemed NOT "trusted".
|
|
** ------------------------------------------------------------- */
|
|
|
|
if (DtHELP_EXECUTE_QUERY_UNALIASED == executionPolicy)
|
|
{
|
|
if ( ! (trusted (hv_path)))
|
|
*ret_queryNeeded = True; /* Query ALL non-trusted help volumes */
|
|
}
|
|
else
|
|
*ret_queryNeeded = (DtHELP_EXECUTE_QUERY_ALL == executionPolicy);
|
|
|
|
/* get whether exec permitted */
|
|
if ( DtHELP_EXECUTE_ALL == executionPolicy
|
|
|| DtHELP_EXECUTE_QUERY_UNALIASED == executionPolicy
|
|
|| DtHELP_EXECUTE_QUERY_ALL == executionPolicy)
|
|
*ret_execPermitted = True;
|
|
else
|
|
*ret_execPermitted = False;
|
|
|
|
/* parse apart the command string, looking for DtHelpExecAlias */
|
|
/* The first call will return true, with the first string */
|
|
token = (char *) commandStr + DtStrspn((char *)commandStr, " \t");
|
|
tokenEnd = token + DtStrcspn(token, " \t");
|
|
tokenEndChar = *tokenEnd;
|
|
if (tokenEnd) *tokenEnd = EOS;
|
|
|
|
if ( NULL == token || _DtHelpCeStrCaseCmpLatin1(token, ExecAliasCmd) != 0 )
|
|
{
|
|
/*** the command is not an alias; proceed using execution Policy ***/
|
|
|
|
*tokenEnd = tokenEndChar; /* restore the string */
|
|
|
|
*ret_cmdStr = strdup(commandStr);
|
|
ret = *ret_execPermitted;
|
|
|
|
return ret; /* RETURN ret */
|
|
}
|
|
|
|
|
|
/**** It's an alias; get it , look it up, and return it ****/
|
|
|
|
*tokenEnd = tokenEndChar; /* restore the string */
|
|
|
|
/* get the next token */
|
|
token = tokenEnd + DtStrspn(tokenEnd, " \t");
|
|
tokenEnd = token + DtStrcspn(token, " \t");
|
|
tokenEndChar = *tokenEnd;
|
|
if (tokenEnd) *tokenEnd = EOS;
|
|
|
|
if ( token )
|
|
{
|
|
Display * dpy = XtDisplay(helpWidget);
|
|
XrmDatabase appDb = XrmGetDatabase(dpy);
|
|
XrmValue value;
|
|
String appname, appclass;
|
|
char * reptype;
|
|
char *rsrc_name, *rsrc_class;
|
|
|
|
rsrc_name = XtMalloc(200);
|
|
rsrc_class = XtMalloc(200);
|
|
|
|
XtGetApplicationNameAndClass(dpy,&appname,&appclass);
|
|
|
|
/* query the application's database for the alias command */
|
|
/* build alias resource class and resource */
|
|
/* e.g. App.executionAlias.<alias> */
|
|
sprintf(rsrc_name,"%s.%s.%s",appclass, RN_execAlias,token);
|
|
/* e.g. App.ExecutionAlias.<alias> */
|
|
sprintf(rsrc_class,"%s.%s.%s",appclass, RC_execAlias,token);
|
|
|
|
/* Get alias command */
|
|
if (XrmGetResource(appDb,rsrc_name,rsrc_class,&reptype,&value) == True)
|
|
aliasCommand = value.addr;
|
|
|
|
/* build alias resource name and resource */
|
|
/* e.g. app.executionAlias.<alias> */
|
|
sprintf(rsrc_name,"%s.%s.%s",appname, RN_execAlias,token);
|
|
/* e.g. app.ExecutionAlias.<alias> */
|
|
sprintf(rsrc_class,"%s.%s.%s",appname, RC_execAlias,token);
|
|
|
|
/* Get alias command and override class with instance, if defined */
|
|
if (XrmGetResource(appDb,rsrc_name,rsrc_class,&reptype,&value) == True)
|
|
aliasCommand = value.addr;
|
|
|
|
if (rsrc_name) XtFree(rsrc_name);
|
|
if (rsrc_class) XtFree(rsrc_class);
|
|
} /* if alias token */
|
|
else
|
|
{
|
|
token = "";
|
|
}
|
|
|
|
if (tokenEnd) *tokenEnd = tokenEndChar; /* restore the string */
|
|
|
|
/* alias was defined */
|
|
if (aliasCommand)
|
|
{
|
|
*ret_cmdStr = strdup(aliasCommand);
|
|
/* see if query needed; is not if policy is query_unaliased or all */
|
|
*ret_queryNeeded = !( DtHELP_EXECUTE_QUERY_UNALIASED == executionPolicy
|
|
|| DtHELP_EXECUTE_ALL == executionPolicy);
|
|
ret = *ret_execPermitted;
|
|
}
|
|
else /* the alias wasn't defined */
|
|
{
|
|
char * aliasToken = token; /* token currently pts to alias */
|
|
|
|
/* look for a default command */
|
|
/* get the next token */
|
|
token = tokenEnd + DtStrspn(tokenEnd, " \t");
|
|
tokenEnd = token + DtStrcspn(token, " \t");
|
|
|
|
if (token == tokenEnd)
|
|
{ /* alias wasn't defined and no default command */
|
|
*ret_cmdStr = strdup(aliasToken);
|
|
*ret_invalidAlias = True;
|
|
*ret_queryNeeded = False; /* no query needed on invalid alias, ever */
|
|
*ret_execPermitted = False; /* can't exec an invalid alias */
|
|
ret = False;
|
|
}
|
|
else
|
|
{ /* alias wasn't defined but a default command */
|
|
/* query is whatever was determined earlier */
|
|
*ret_cmdStr = strdup(token);
|
|
ret = *ret_execPermitted;
|
|
}
|
|
}
|
|
|
|
return ret; /* RETURN ret */
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _DtHelpCeWaitAndProcessEvents
|
|
*
|
|
* Purpose:
|
|
* _DtHelpCeWaitAndProcessEvents will process events and call
|
|
* the waitProc until waitProc returns False. This function
|
|
* is useful to put up modal dialogs that must be reponded to
|
|
* in the midst of executing code that must remain on the call stack.
|
|
*
|
|
* Warning:
|
|
* This function should only be used on modal dialogs.
|
|
*
|
|
*********************************************************************/
|
|
|
|
void
|
|
_DtHelpCeWaitAndProcessEvents (
|
|
Widget w,
|
|
_DtHelpCeWaitProc waitProc,
|
|
void * clientData)
|
|
{
|
|
Boolean waitFlag;
|
|
XEvent event;
|
|
XtAppContext app;
|
|
|
|
app = XtWidgetToApplicationContext(w);
|
|
do
|
|
{
|
|
#ifndef XTHREADS
|
|
XtAppNextEvent(app,&event);
|
|
XtDispatchEvent(&event);
|
|
#else
|
|
XtInputMask mask;
|
|
|
|
while (!(mask = XtAppPending(app)))
|
|
; /* Busy waiting - so we don't lose our Lock! */
|
|
|
|
if (mask & XtIMXEvent) /* We have an XEvent */
|
|
{
|
|
/* Get the XEvent - we know it's there! Note that XtAppNextEvent
|
|
would also process timers/alternate inputs.
|
|
*/
|
|
XtAppNextEvent(app, &event); /* No blocking, since an event is ready */
|
|
XtDispatchEvent(&event);
|
|
}
|
|
else /* Not a XEvent, it's an alternate input/timer event */
|
|
{
|
|
XtAppProcessEvent(app, mask); /* No blocking, since an event is ready */
|
|
}
|
|
#endif /* XTHREADS */
|
|
/* check to see if we're done waiting */
|
|
waitFlag = (*waitProc)(w, clientData);
|
|
} while (waitFlag);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Function: WaitForBtnActivatedCB
|
|
*
|
|
* Purpose:
|
|
* Treats the 'clientData' as a pointer to an integer
|
|
* and turns its value into a Boolean
|
|
*
|
|
* Returns: *(int *)clientData < 0
|
|
*****************************************************************************/
|
|
static Boolean
|
|
WaitForBtnActivatedCB(
|
|
Widget w,
|
|
void * clientData)
|
|
{
|
|
return (*(int *)clientData < 0);
|
|
/* True=keep waiting; False= wait no longer */
|
|
}
|
|
|
|
|
|
typedef struct ModalMsgDlgCBStruct
|
|
{
|
|
Widget msgDlg;
|
|
Widget okBtn;
|
|
Widget cancelBtn;
|
|
Widget helpBtn;
|
|
int activatedBtnId;
|
|
} ModalMsgDlgCBStruct;
|
|
|
|
/*****************************************************************************
|
|
* Function: IdentifyActivatedBtnCB
|
|
*
|
|
* Purpose:
|
|
* Treats the 'clientData' as a pointer to an integer.
|
|
* Waits for the value pointed to by clientData to be >= 0.
|
|
*
|
|
*****************************************************************************/
|
|
static void
|
|
IdentifyActivatedBtnCB(
|
|
Widget w,
|
|
XtPointer clientData,
|
|
XtPointer callData)
|
|
{
|
|
ModalMsgDlgCBStruct * pMd = (ModalMsgDlgCBStruct *) clientData;
|
|
|
|
/* w must be a XmMessageDialog widget */
|
|
if (pMd->okBtn == w)
|
|
{ pMd->activatedBtnId = XmDIALOG_OK_BUTTON; return; /* RETURN */ }
|
|
if (pMd->cancelBtn == w)
|
|
{ pMd->activatedBtnId = XmDIALOG_CANCEL_BUTTON; return; /* RETURN */ }
|
|
if (pMd->helpBtn == w)
|
|
{ pMd->activatedBtnId = XmDIALOG_HELP_BUTTON; return; /* RETURN */ }
|
|
pMd->activatedBtnId = -1; /* unknown button */
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: _DtHelpFilterExecCmd
|
|
*
|
|
* Args:
|
|
* helpWidget: help widget requesting to exec the command
|
|
* command: command string to execute
|
|
* execPolicy: current policy setting
|
|
* useQueryDialog: use a dialog to query user whether to exec, if not allowed
|
|
* pHelpStuff: ptr to the HelpStuff structure of the help widget
|
|
* ret_filteredCmdStr: filtered command string
|
|
*
|
|
* Returns:
|
|
* 0: no error; filteredCmdStr can be exec'd
|
|
* -1: error: either internal or executionPolicy denies exec;
|
|
* filteredCmdStr is NULL
|
|
*
|
|
* Purpose:
|
|
* This function filters an execution command string. This can
|
|
* occur in several ways. In all cases, the command string is
|
|
* supports command alias replacement. If the final outcome
|
|
* is that execution is permitted, the returned string is
|
|
* is the command string to execute. If execution is not
|
|
* permitted, the return string is a NULL pointer.
|
|
*
|
|
* Filtering of the command occurs as follows.
|
|
* If executionPolicy permits execution, only alias replacement occurs.
|
|
* If executionPolicy does restrict execution and a
|
|
* dialog is requested, then a modal dialog is posted and the
|
|
* user can decide whether to execute or not.
|
|
* If a dialog is not requested, the return string is NULL.
|
|
*
|
|
* Comments:
|
|
* This code is written such that we don't need nor want to know
|
|
* whether it is a general or quick help widget.
|
|
*
|
|
* Warning:
|
|
* command string must be writable; it is written, but left
|
|
* unchanged whent the function exits.
|
|
*
|
|
* This operation is synchronous, meaning that, if a dialog is
|
|
* posted, it is a modal dialog and the function won't return
|
|
* until the user selects a button.
|
|
*
|
|
* Called by:
|
|
*****************************************************************************/
|
|
int _DtHelpFilterExecCmd(
|
|
Widget helpWidget,
|
|
const char * commandStr,
|
|
unsigned char executionPolicy,
|
|
Boolean useQueryDialog,
|
|
_DtHelpCommonHelpStuff * pHelpStuff,
|
|
char * * ret_filteredCmdStr,
|
|
char * hv_path)
|
|
{
|
|
ModalMsgDlgCBStruct msgDlgCBStruct;
|
|
Boolean goodCmd;
|
|
Boolean invalidAlias;
|
|
Boolean execPermitted;
|
|
Boolean queryNeeded;
|
|
Widget msgDlg;
|
|
char * filteredCmdStr = NULL;
|
|
Arg args[5];
|
|
int n;
|
|
XmString labelString;
|
|
XmString labelString2;
|
|
Widget noexecBtn;
|
|
Widget execBtn;
|
|
|
|
goodCmd = _DtHelpFilterExecCmdStr(helpWidget, executionPolicy,
|
|
commandStr, &filteredCmdStr, &invalidAlias,
|
|
&execPermitted, &queryNeeded, hv_path);
|
|
|
|
/* if permissions allow immediate execution, do so */
|
|
if (execPermitted && False == queryNeeded)
|
|
{
|
|
*ret_filteredCmdStr = filteredCmdStr;
|
|
return 0; /* RETURN ok */
|
|
}
|
|
|
|
if (False == useQueryDialog)
|
|
{
|
|
*ret_filteredCmdStr = NULL;
|
|
return -1; /* RETURN error */
|
|
}
|
|
|
|
/* create the dialog, but don't yet manage it */
|
|
msgDlg = CreateExecErrorDlg(helpWidget,filteredCmdStr,
|
|
invalidAlias,pHelpStuff, NO_CONDITION, "");
|
|
|
|
/* if a bad alias or no exec permitted,
|
|
don't need to wait for a response; dlg has close & Help */
|
|
if (False == execPermitted || False == queryNeeded)
|
|
{
|
|
XtManageChild(msgDlg); /* manage modeless dialog */
|
|
*ret_filteredCmdStr = NULL; /* don't execute */
|
|
XtFree(filteredCmdStr);
|
|
return -1; /* RETURN error */
|
|
}
|
|
|
|
/* if got this far, query is needed;make the dialog include
|
|
Execute Anyway and Don't Execute buttons */
|
|
|
|
/* give the right title to the buttons */
|
|
labelString = XmStringCreateLocalized(((char *)_DTGETMESSAGE
|
|
(HUSET, 10,"Execute Anyway")));
|
|
/* give the right title to the Cancel button */
|
|
labelString2 = XmStringCreateLocalized(((char *)_DTGETMESSAGE
|
|
(HUSET, 11,"Don't Execute")));
|
|
n = 0;
|
|
XtSetArg (args[n], XmNokLabelString, labelString); n++;
|
|
XtSetArg (args[n], XmNcancelLabelString, labelString2); n++;
|
|
XtSetArg (args[n], XmNdialogStyle, XmDIALOG_PRIMARY_APPLICATION_MODAL); n++;
|
|
XtSetValues(msgDlg,args,n);
|
|
XmStringFree(labelString);
|
|
XmStringFree(labelString2);
|
|
|
|
/* We put an activate callback on the DontExecute and ExecuteAnyway buttons
|
|
and wait until a button is pressed. */
|
|
noexecBtn = XmMessageBoxGetChild (msgDlg, XmDIALOG_CANCEL_BUTTON);
|
|
XtAddCallback(noexecBtn, XmNactivateCallback,
|
|
IdentifyActivatedBtnCB, (XtPointer) &msgDlgCBStruct);
|
|
|
|
execBtn = XmMessageBoxGetChild (msgDlg, XmDIALOG_OK_BUTTON);
|
|
XtManageChild (execBtn); /* re-manage the button */
|
|
XtAddCallback(execBtn, XmNactivateCallback,
|
|
IdentifyActivatedBtnCB, (XtPointer) &msgDlgCBStruct);
|
|
|
|
/* fill out the CB information structure used by IdentifyActivatedBtnCB */
|
|
msgDlgCBStruct.msgDlg = msgDlg;
|
|
msgDlgCBStruct.okBtn = execBtn;
|
|
msgDlgCBStruct.cancelBtn = noexecBtn;
|
|
msgDlgCBStruct.activatedBtnId = -1;
|
|
|
|
/* Display message dialog */
|
|
XtManageChild (msgDlg);
|
|
|
|
/*
|
|
* turn on the modal dialog indicator
|
|
*/
|
|
_DtHelpTurnOnNoEnter(helpWidget);
|
|
|
|
/* wait until 'msgDlgCBStruct.activatedBtnId' has a value >= 0 */
|
|
/* this occurs when the user responds to the msg dialog */
|
|
_DtHelpCeWaitAndProcessEvents(msgDlg,
|
|
WaitForBtnActivatedCB, &msgDlgCBStruct.activatedBtnId);
|
|
|
|
/*
|
|
* turn off the modal dialog indicator
|
|
*/
|
|
_DtHelpTurnOffNoEnter(helpWidget);
|
|
|
|
/* no need to destroy msgDlg; it has a closeCallback to do that */
|
|
|
|
/* act based on which button was activated */
|
|
if (msgDlgCBStruct.activatedBtnId == XmDIALOG_OK_BUTTON)
|
|
{
|
|
*ret_filteredCmdStr = filteredCmdStr; /* do execute command */
|
|
return 0; /* RETURN ok */
|
|
}
|
|
else
|
|
{
|
|
*ret_filteredCmdStr = NULL; /* don't execute */
|
|
XtFree(filteredCmdStr);
|
|
return -1; /* RETURN error */
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Function: _DtHelpExecFilteredCmd
|
|
*
|
|
* Args:
|
|
* helpWidget: help widget requesting to exec the command
|
|
* command: command string to execute
|
|
* modal: is the execution modal (sync) or modeless (async)
|
|
* helpLocationId: helpOnHelp file location for Help btn in error dialog
|
|
* pDisplayStuff: ptr to the DisplayWidget stuff of the help widget
|
|
* pHelpStuff: ptr to the CommonHelp stuff of the help widget
|
|
*
|
|
* Comments:
|
|
* This code is written such that we don't need nor want to know
|
|
* whether it is a general or quick help widget.
|
|
*
|
|
* Warning:
|
|
* command string must be writable; it is written, but left
|
|
* unchanged whent the function exits.
|
|
*
|
|
* At the moment, the helpLocationId is ignored, and the
|
|
* help location is hardwired to DtHELP_ExecutionPolicy_STR
|
|
* in CreateExecErrorDialog().
|
|
* Called by:
|
|
*****************************************************************************/
|
|
void _DtHelpExecFilteredCmd(
|
|
Widget helpWidget,
|
|
char * commandStr,
|
|
char * helpLocationId,
|
|
_DtHelpDisplayWidgetStuff * pDisplayStuff,
|
|
_DtHelpCommonHelpStuff * pHelpStuff)
|
|
{
|
|
Boolean goodCmd;
|
|
Boolean invalidAlias;
|
|
Boolean execPermitted;
|
|
Boolean queryNeeded;
|
|
char * filteredCmdStr = NULL;
|
|
ExecContext * execContext;
|
|
XmString labelString;
|
|
XmString labelString2;
|
|
Widget msgDlg;
|
|
Widget btn;
|
|
int n;
|
|
Arg args[5];
|
|
char *hv_path=NULL;
|
|
|
|
uid_t user;
|
|
char *home_dir;
|
|
Boolean diff_home_dirs=False; /* True ==> $HOME is different from */
|
|
/* root user's $HOME directory */
|
|
/*
|
|
getpw{uid,nam}_r routines fail on IBM platform when search password info
|
|
via NIS (yellow pages). However, in the case of root, we'll assume that
|
|
the password info is in /etc/passwd. If this is not the case, the
|
|
following code can fail on IBM platform when XTHREADS and XUSE_MTSAFE_API
|
|
are defined.
|
|
*/
|
|
/*
|
|
_Xgetpwparams pwd_buf;
|
|
*/
|
|
struct passwd * pwd_ret;
|
|
|
|
/** -------------------------------------------------------------- *
|
|
** If we're running as the root user
|
|
** o check if the value of the HOME env var matches
|
|
** root's home directory (defined by /etc/passwd).
|
|
** o If they do not match, then present a dialog
|
|
** alerting the user of this, along with the command to
|
|
** invoke.
|
|
** -------------------------------------------------------------- */
|
|
|
|
if ( (user=getuid()) == ROOT_USER)
|
|
{
|
|
home_dir = getenv ("HOME");
|
|
|
|
if (home_dir != NULL && strlen(home_dir) >= (size_t) 1)
|
|
{
|
|
if (((pwd_ret = _XGetpwuid(user, pwd_buf)) != NULL)
|
|
&& (strcmp(home_dir, pwd_ret->pw_dir)))
|
|
{
|
|
diff_home_dirs = True;
|
|
}
|
|
}
|
|
}
|
|
|
|
hv_path = _DtHelpFileLocate(DtHelpVOLUME_TYPE, pDisplayStuff->helpVolume,
|
|
_DtHelpFileSuffixList, False, R_OK);
|
|
|
|
/* The desired and intended behaviour is to use _DtHelpFilterExecCmdStr(), but
|
|
the other code is left here, should a change be wished. */
|
|
#if 1
|
|
/* This function runs a filter for policy and alias but posts no dialog */
|
|
goodCmd=_DtHelpFilterExecCmdStr(helpWidget,
|
|
pDisplayStuff->executionPolicy, commandStr,
|
|
&filteredCmdStr, &invalidAlias, &execPermitted, &queryNeeded, hv_path);
|
|
#else
|
|
/* This function does an synchronous filter; i.e. the code runs a filter
|
|
for policy and alias, and if policy denies exec and the command is
|
|
valid, then posts a modal dialog and waits for the user to decide
|
|
what to do before returning. */
|
|
goodCmd = _DtHelpFilterExecCmd(helpWidget, commandStr,
|
|
pDisplayStuff->executionPolicy, True,
|
|
pHelpStuff, &filteredCmdStr, hv_path);
|
|
execPermitted = (goodCmd == 0); /* convert an error int into a Boolean */
|
|
queryNeeded =
|
|
(( pDisplayStuff->executionPolicy==DtHELP_EXECUTE_QUERY_ALL)
|
|
|| (pDisplayStuff->executionPolicy==DtHELP_EXECUTE_QUERY_UNALIASED))
|
|
&& goodCmd;
|
|
#endif
|
|
|
|
/* if permissions allow immediate execution, do so */
|
|
if (execPermitted && False == queryNeeded && diff_home_dirs == False)
|
|
{
|
|
(void) _DtHelpExecProcedure (pHelpStuff->pDisplayArea, filteredCmdStr);
|
|
free(filteredCmdStr);
|
|
return; /* RETURN */
|
|
}
|
|
|
|
/* this traps bad cmds and also use of the synchronous filter call */
|
|
if (NULL == filteredCmdStr) return; /* RETURN */
|
|
|
|
/*** Create a modeless dialog to inform the user of the problem
|
|
and possibly allow them to execute the command anyway. ***/
|
|
|
|
/* create the dialog, but don't yet manage it */
|
|
if ( diff_home_dirs == True)
|
|
msgDlg = CreateExecErrorDlg(helpWidget,filteredCmdStr, invalidAlias,pHelpStuff,
|
|
MISMATCHING_HOME_DIRS, home_dir );
|
|
else
|
|
msgDlg = CreateExecErrorDlg(helpWidget,filteredCmdStr, invalidAlias,pHelpStuff,
|
|
NO_CONDITION, "");
|
|
|
|
|
|
/*** setup ExecuteAnyway and Help buttons ***/
|
|
|
|
if ( (diff_home_dirs == True)
|
|
||
|
|
(queryNeeded && execPermitted) )
|
|
{
|
|
/* give the right title to the buttons */
|
|
labelString = XmStringCreateLocalized(((char *)_DTGETMESSAGE
|
|
(HUSET, 10,"Execute Anyway")));
|
|
/* give the right title to the Cancel button */
|
|
labelString2 = XmStringCreateLocalized(((char *)_DTGETMESSAGE
|
|
(HUSET, 11,"Don't Execute")));
|
|
n = 0;
|
|
XtSetArg (args[n], XmNokLabelString, labelString); n++;
|
|
XtSetArg (args[n], XmNcancelLabelString, labelString2); n++;
|
|
XtSetValues(msgDlg,args,n);
|
|
XmStringFree(labelString);
|
|
XmStringFree(labelString2);
|
|
|
|
btn = XmMessageBoxGetChild (msgDlg, XmDIALOG_OK_BUTTON);
|
|
XtManageChild (btn); /* re-manage the button */
|
|
|
|
/* add the ExecuteContextCB() client-data and callback */
|
|
execContext = malloc(sizeof(ExecContext));
|
|
if (execContext)
|
|
{
|
|
execContext->command = filteredCmdStr;
|
|
execContext->pDisplayArea = pHelpStuff->pDisplayArea;
|
|
XtAddCallback(btn, XmNactivateCallback,
|
|
ExecuteContextCB, (XtPointer) execContext);
|
|
}
|
|
else
|
|
{
|
|
free(filteredCmdStr);
|
|
}
|
|
} /* cmd wasn't an alias */
|
|
else
|
|
{
|
|
free(filteredCmdStr);
|
|
}
|
|
|
|
/* Display message dialog */
|
|
XtManageChild (msgDlg);
|
|
|
|
/* RETURN */
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: LocateWidgetId()
|
|
*
|
|
*
|
|
*
|
|
* Called by:
|
|
*****************************************************************************/
|
|
static Widget LocateWidgetId(
|
|
Display *dpy,
|
|
int screen,
|
|
int *statusRet,
|
|
Widget shellWidget,
|
|
Cursor cursorIn)
|
|
{
|
|
static Cursor DfltOnItemCursor = 0;
|
|
Widget widget;
|
|
Widget child;
|
|
CompositeWidget comp_widget;
|
|
int status;
|
|
Cursor cursor;
|
|
XEvent event;
|
|
int x,y;
|
|
int i;
|
|
Window parent;
|
|
Window sub;
|
|
Window target_win;
|
|
int new_x, new_y;
|
|
int offset;
|
|
KeySym keySym;
|
|
Boolean notDone=TRUE;
|
|
|
|
|
|
/* Make the target cursor */
|
|
if (cursorIn != 0)
|
|
cursor = cursorIn;
|
|
else
|
|
#if 0
|
|
cursor = XCreateFontCursor (dpy, XC_question_arrow);
|
|
#else
|
|
{
|
|
_DtHelpProcessLock();
|
|
if (0 == DfltOnItemCursor)
|
|
{
|
|
char *bits;
|
|
char *maskBits;
|
|
unsigned int width;
|
|
unsigned int height;
|
|
unsigned int xHotspot;
|
|
unsigned int yHotspot;
|
|
Pixmap pixmap;
|
|
Pixmap maskPixmap;
|
|
XColor xcolors[2];
|
|
|
|
width = onitem32_width;
|
|
height = onitem32_height;
|
|
bits = (char *) onitem32_bits;
|
|
maskBits = (char *) onitem32_m_bits;
|
|
xHotspot = onitem32_x_hot;
|
|
yHotspot = onitem32_y_hot;
|
|
|
|
pixmap = XCreateBitmapFromData (dpy,
|
|
RootWindowOfScreen(XtScreen(shellWidget)), bits,
|
|
width, height);
|
|
|
|
|
|
maskPixmap = XCreateBitmapFromData (dpy,
|
|
RootWindowOfScreen(XtScreen(shellWidget)), maskBits,
|
|
width, height);
|
|
|
|
xcolors[0].pixel = BlackPixelOfScreen(ScreenOfDisplay(dpy, screen));
|
|
xcolors[1].pixel = WhitePixelOfScreen(ScreenOfDisplay(dpy, screen));
|
|
|
|
XQueryColors(dpy,
|
|
DefaultColormapOfScreen(ScreenOfDisplay(dpy, screen)),
|
|
xcolors,
|
|
2);
|
|
|
|
DfltOnItemCursor = XCreatePixmapCursor (dpy, pixmap, maskPixmap,
|
|
&(xcolors[0]), &(xcolors[1]),
|
|
xHotspot, yHotspot);
|
|
XFreePixmap (dpy, pixmap);
|
|
XFreePixmap (dpy, maskPixmap);
|
|
} /* if dflt cursor not yet created */
|
|
cursor = DfltOnItemCursor;
|
|
_DtHelpProcessUnlock();
|
|
} /* if to use the standard cursor */
|
|
#endif
|
|
|
|
|
|
/* Grab the pointer using target cursor, letting it roam all over */
|
|
status = XtGrabPointer (shellWidget, TRUE,
|
|
ButtonPressMask|ButtonReleaseMask, GrabModeAsync,
|
|
GrabModeAsync, None, cursor, CurrentTime);
|
|
if (status != GrabSuccess)
|
|
{
|
|
XmeWarning(shellWidget,(char *)_DTGETMESSAGE(HUSET, 3,
|
|
"Internal Error: Could not grab the mouse\nDtHelpReturnSelectedWidget aborted.\n"));
|
|
*statusRet = DtHELP_SELECT_ERROR;
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|
|
/* Grab the Keyboard so we can catch the ESC button press */
|
|
status = XtGrabKeyboard(shellWidget, False,
|
|
GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
if (status != GrabSuccess)
|
|
{
|
|
|
|
XtUngrabPointer (shellWidget, CurrentTime);
|
|
XmeWarning(shellWidget,(char *)_DTGETMESSAGE(HUSET, 4,
|
|
"Internal Error: Could not grab the keyboard\nDtHelpReturnSelectedWidget() aborted.\n"));
|
|
*statusRet = DtHELP_SELECT_ERROR;
|
|
return(NULL);
|
|
}
|
|
|
|
/* We are ok so let the user select a window... */
|
|
while (notDone)
|
|
{
|
|
XtAppContext app = XtWidgetToApplicationContext(shellWidget);
|
|
/* allow one more event */
|
|
#ifndef XTHREADS
|
|
XtAppNextEvent(app, &event);
|
|
#else
|
|
XtInputMask mask;
|
|
|
|
while (!(mask = XtAppPending(app)))
|
|
; /* Busy waiting - so we don't lose our Lock! */
|
|
|
|
if (!(mask & XtIMXEvent)) /* Not a XEvent, it's an alternate input/timer event */
|
|
XtAppProcessEvent(app, mask); /* No blocking, since an event is ready */
|
|
else /* We have an XEvent */
|
|
{
|
|
/* Get the XEvent - we know it's there! Note that XtAppNextEvent
|
|
would also process timers/alternate inputs.
|
|
*/
|
|
XtAppNextEvent(app, &event);
|
|
#endif /* XTHREADS */
|
|
widget = XtWindowToWidget(dpy, event.xany.window);
|
|
|
|
switch (event.type) {
|
|
case ButtonPress:
|
|
break;
|
|
case ButtonRelease:
|
|
notDone = FALSE;
|
|
break;
|
|
case KeyPress:
|
|
/* Look for ESC key press and stop if we get one */
|
|
if (event.xkey.state & ShiftMask)
|
|
offset = 1;
|
|
else
|
|
offset = 0;
|
|
|
|
keySym = XLookupKeysym((XKeyEvent *)&event, offset);
|
|
if (keySym == XK_Escape)
|
|
{
|
|
XtUngrabKeyboard (shellWidget, CurrentTime);
|
|
XtUngrabPointer (shellWidget, CurrentTime);
|
|
*statusRet = DtHELP_SELECT_ABORT;
|
|
return(NULL);
|
|
}
|
|
default:
|
|
XtDispatchEvent(&event);
|
|
}
|
|
#ifdef XTHREADS
|
|
} /* if */
|
|
#endif
|
|
}
|
|
|
|
XtUngrabKeyboard (shellWidget, CurrentTime); /* Done with keyboard */
|
|
XtUngrabPointer (shellWidget, CurrentTime); /* Done with pointer */
|
|
|
|
/* If its null then the user selected some area outside our window(s) */
|
|
if (widget == shellWidget)
|
|
{
|
|
*statusRet = DtHELP_SELECT_INVALID;
|
|
return (NULL);
|
|
}
|
|
if (!XtIsComposite (widget))
|
|
{
|
|
*statusRet = DtHELP_SELECT_VALID;
|
|
return (widget);
|
|
}
|
|
|
|
/* Get the x and y and parent relative to the current window */
|
|
parent = RootWindow(dpy, screen);
|
|
target_win = XtWindow(widget);
|
|
x = event.xbutton.x_root;
|
|
y = event.xbutton.y_root;
|
|
|
|
XTranslateCoordinates(dpy, parent, target_win, x, y,
|
|
&new_x, &new_y, &sub);
|
|
x = new_x;
|
|
y = new_y;
|
|
|
|
comp_widget = (CompositeWidget)widget;
|
|
|
|
/* look for gadgets at this point */
|
|
for (i = 0; i < comp_widget->composite.num_children; i++) {
|
|
child = comp_widget->composite.children[i];
|
|
/* put in check for only managed widgets here */
|
|
if(XtIsManaged(child))
|
|
if (PT_IN_CHILD (x, y, child))
|
|
{
|
|
*statusRet = DtHELP_SELECT_VALID;
|
|
return (child);
|
|
}
|
|
}
|
|
|
|
*statusRet = DtHELP_SELECT_VALID;
|
|
return (widget);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: Boolean RememberDir(String path)
|
|
*
|
|
* Parameters: path Specifies the path to check.
|
|
*
|
|
* Return Value: Boolean if the path name is good.
|
|
*
|
|
* Description: Use the directory caching mechanism to improve performance
|
|
* by remembering the directories that have already been
|
|
* stat'ed.
|
|
*
|
|
*****************************************************************************/
|
|
static Boolean
|
|
RememberDir(String path)
|
|
{
|
|
int result = 0;
|
|
char *ptr;
|
|
struct stat buf;
|
|
|
|
if (path == NULL || *path == '\0')
|
|
return False;
|
|
|
|
if (_DtHelpCeStrrchr(path, "/", MB_CUR_MAX, &ptr) == 0 && ptr != path)
|
|
{
|
|
*ptr = '\0';
|
|
result = _DtHelpCeCheckAndCacheDir(path);
|
|
*ptr = '/';
|
|
}
|
|
if (result == 0 && access(path, R_OK) == 0 &&
|
|
stat(path, &buf) == 0 && S_ISREG(buf.st_mode))
|
|
return True;
|
|
|
|
return False;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* Function: Boolean _DtHelpResolvePathname(
|
|
*
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value: Boolean.
|
|
*
|
|
*
|
|
* Description: _DtHelpResolvePathname attempts to validate and expand a path
|
|
* to a Cache Creek help access file.
|
|
*
|
|
*****************************************************************************/
|
|
Boolean _DtHelpResolvePathname(
|
|
Widget widget,
|
|
char * * io_fileName,
|
|
_DtHelpVolumeHdl * io_volumeHandle,
|
|
char * sysVolumeSearchPath,
|
|
char * userVolumeSearchPath)
|
|
{
|
|
String newPath = NULL;
|
|
|
|
/* try to locate file and its entry, if present */
|
|
newPath = _DtHelpFileLocate(DtHelpVOLUME_TYPE, *io_fileName,
|
|
_DtHelpFileSuffixList,False,R_OK);
|
|
|
|
/* If we found a valid file let's do some set up here */
|
|
|
|
if (newPath != NULL) /* We have a valid volume file so open it */
|
|
{
|
|
/* Close the current one if we have one open */
|
|
if (*io_volumeHandle != NULL)
|
|
_DtHelpCloseVolume(*io_volumeHandle);
|
|
|
|
/* Open the help volume file and save the handle id */
|
|
if (_DtHelpOpenVolume(newPath,io_volumeHandle) >= 0)
|
|
{
|
|
/* Copy the expanded file location path */
|
|
XtFree(*io_fileName);
|
|
*io_fileName = newPath;
|
|
|
|
return(TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* ERROR; leave io_fileName untouched on error
|
|
* We used to set it to null here now we just return what came in
|
|
*/
|
|
/* later NOTE: this seems strange, since we have closed the
|
|
old volume, invalidating io_fileName */
|
|
XtFree(newPath);
|
|
XmeWarning(widget,(char*)UtilMessage2);
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else /* We have a non-valid path */
|
|
{
|
|
/* ERROR; leave io_fileName untouched on error
|
|
* We used to set it to null here now we just return what came in
|
|
*/
|
|
|
|
XmeWarning(widget,(char*)UtilMessage0);
|
|
return (FALSE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: Boolean _DtHelpExpandHelpVolume(DtHelpDialogWidget nw);
|
|
*
|
|
*
|
|
* Parameters: nw Specifies the current help dialog widget.
|
|
*
|
|
* Return Value: Boolean.
|
|
*
|
|
|
|
* Description: _DtHelpExpandHelpVolume looks for a $LANG variable in the
|
|
* helpAccesFile string and if found, replaces it with the
|
|
* current lang variable.
|
|
*
|
|
*****************************************************************************/
|
|
Boolean _DtHelpExpandHelpVolume(
|
|
Widget w,
|
|
_DtHelpDisplayWidgetStuff * display,
|
|
_DtHelpCommonHelpStuff * help,
|
|
_DtHelpPrintStuff * print)
|
|
{
|
|
Boolean validTopic = FALSE;
|
|
Boolean validPath = FALSE;
|
|
char *topLevelId;
|
|
|
|
/* Free the old, and Copy the new volumeHandle to printVolume varialbe */
|
|
if (print->printVolume != NULL)
|
|
XtFree(print->printVolume);
|
|
|
|
print->printVolume = XtNewString(display->helpVolume);
|
|
|
|
validPath = _DtHelpResolvePathname((Widget)w,
|
|
&print->printVolume,
|
|
&display->volumeHandle,
|
|
help->sysVolumeSearchPath,
|
|
help->userVolumeSearchPath);
|
|
|
|
|
|
/* Check to see that we resolved our path correctly */
|
|
if (!validPath)
|
|
return (FALSE); /* RETURN */
|
|
else
|
|
{
|
|
|
|
/* The following routine will malloc memory for the topLevelId
|
|
* variable, so we must free our current version first.
|
|
*/
|
|
XtFree(help->topLevelId);
|
|
|
|
/* Assign our top level topic for this help access file */
|
|
validTopic = _DtHelpCeGetTopTopicId(display->volumeHandle, &topLevelId);
|
|
|
|
if (!validTopic)
|
|
{
|
|
/* Bad top level topic */
|
|
help->topLevelId = NULL;
|
|
return(FALSE);
|
|
}
|
|
else
|
|
{
|
|
/* recall that the topLevelId/File vars are malloc'd */
|
|
help->topLevelId = topLevelId;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: char *_DtHelpParseIdString(char * specification);
|
|
*
|
|
*
|
|
* Parameters: specification Specifies an author defined help topic.
|
|
*
|
|
* Return Value: Void.
|
|
*
|
|
* Description: This function copies the locationId portion of the
|
|
* specification and returns it to the calling routine.
|
|
*
|
|
*****************************************************************************/
|
|
char *_DtHelpParseIdString(
|
|
char *specification)
|
|
{
|
|
|
|
char *pAccessFile = NULL;
|
|
char *tmpSpec=NULL;
|
|
char *returnStr=NULL;
|
|
char *strtok_ptr;
|
|
|
|
tmpSpec = XtNewString(specification);
|
|
|
|
|
|
/* First look for a blank in the specification. This will signify that
|
|
* we have a HelpAccessFile as part of the specification.
|
|
*/
|
|
|
|
/* The first call will return true, with the first string */
|
|
pAccessFile = DtStrtok_r(tmpSpec, " ", &strtok_ptr);
|
|
returnStr = XtNewString(pAccessFile);
|
|
|
|
/* The second call will return true only if we have another string */
|
|
pAccessFile = DtStrtok_r(NULL, " ", &strtok_ptr);
|
|
|
|
if (pAccessFile != NULL)
|
|
{
|
|
/* We have a helpAccessFile in our specification */
|
|
|
|
XtFree(returnStr);
|
|
|
|
returnStr = XtNewString(pAccessFile);
|
|
XtFree(tmpSpec);
|
|
return(returnStr);
|
|
|
|
}
|
|
else
|
|
{
|
|
/* We don't have a helpAccessFile as part of the specificaiton
|
|
* so we just return our locationId.
|
|
*/
|
|
XtFree(tmpSpec);
|
|
return (returnStr);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: char *_DtHelpParseAccessFile(char * specification);
|
|
*
|
|
*
|
|
* Parameters: specification Specifies an author defined help topic.
|
|
*
|
|
* Return Value: Void.
|
|
*
|
|
* Description: This function copies the helpAccessFile portion of the
|
|
* specification and returns it to the calling routine.
|
|
*
|
|
*****************************************************************************/
|
|
char *_DtHelpParseAccessFile(
|
|
char *specification)
|
|
{
|
|
char *pAccessFile = NULL;
|
|
char *tmpSpec=NULL;
|
|
char *returnStr=NULL;
|
|
char *strtok_ptr;
|
|
|
|
tmpSpec = XtNewString(specification);
|
|
|
|
|
|
/* First look for a blank in the specification. This will signify that
|
|
* we have a HelpAccessFile as part of the specification.
|
|
*/
|
|
|
|
/* The first call will return true, with the first string */
|
|
pAccessFile = DtStrtok_r(tmpSpec, " ", &strtok_ptr);
|
|
returnStr = XtNewString(pAccessFile);
|
|
|
|
/* The second call will return true only if we have another string */
|
|
pAccessFile = DtStrtok_r(NULL, " ", &strtok_ptr);
|
|
|
|
if (pAccessFile != NULL)
|
|
{
|
|
/* We have a helpAccessFile in our specification */
|
|
|
|
|
|
/* If we have an accessFile, but it's not a full path, then we
|
|
* must get the full path from the reg file.
|
|
*/
|
|
XtFree(tmpSpec);
|
|
return(returnStr);
|
|
|
|
}
|
|
else
|
|
{
|
|
/* We don't have a helpAccessFile as part of the specificaiton
|
|
* so return NULL.
|
|
*/
|
|
XtFree(returnStr);
|
|
XtFree(tmpSpec);
|
|
return (NULL);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: DtHelpReturnSelectedWidgetId
|
|
*
|
|
* Parameters: parent Specifies the widget ID to use as the bases of
|
|
* interaction, usually a top level shell.
|
|
*
|
|
* cursor Specifies the cursor to be used for the pointer
|
|
* during the interaction. If a value of NULL is
|
|
* used this function will use a default cursor
|
|
* value.
|
|
*
|
|
* widget This is the return value (e.g. the selected
|
|
* widget). A value of NULL is returned on error.
|
|
*
|
|
* Return Value: Status: (-1,0 or 1).
|
|
*
|
|
* Purpose: Allows developers to get the widget ID for any widget in their UI
|
|
* that the user has selected via the pointer. This function will
|
|
* cause the cursor to change and allow a user to select an item in
|
|
* the UI.
|
|
*
|
|
*****************************************************************************/
|
|
int DtHelpReturnSelectedWidgetId(
|
|
Widget parent,
|
|
Cursor cursor,
|
|
Widget *widget)
|
|
{
|
|
|
|
|
|
Display *dpy;
|
|
int screen;
|
|
Widget selectedItem;
|
|
int status=DtHELP_SELECT_ERROR;
|
|
Screen *retScr;
|
|
int result;
|
|
_DtHelpWidgetToAppContext(parent);
|
|
|
|
_DtHelpAppLock(app);
|
|
/* Setup some needed variables */
|
|
dpy = XtDisplay(parent);
|
|
retScr = XtScreen(parent);
|
|
|
|
screen = XScreenNumberOfScreen(retScr);
|
|
|
|
/* refresh the display */
|
|
XmUpdateDisplay(parent);
|
|
|
|
/* Change the curser to let the user select the desired widget */
|
|
selectedItem = LocateWidgetId(dpy, screen, &status, parent, cursor);
|
|
|
|
switch (status)
|
|
{
|
|
case DtHELP_SELECT_VALID:
|
|
*widget = selectedItem;
|
|
result = DtHELP_SELECT_VALID;
|
|
break;
|
|
|
|
case DtHELP_SELECT_ABORT:
|
|
*widget = NULL;
|
|
result = DtHELP_SELECT_ABORT;
|
|
break;
|
|
|
|
case DtHELP_SELECT_ERROR:
|
|
*widget = NULL;
|
|
result = DtHELP_SELECT_ERROR;
|
|
break;
|
|
|
|
case DtHELP_SELECT_INVALID:
|
|
default:
|
|
*widget = NULL;
|
|
result = DtHELP_SELECT_INVALID;
|
|
break;
|
|
}
|
|
|
|
_DtHelpAppUnlock(app);
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpTopicListAddToHead(
|
|
* char *locationId,
|
|
* int topicType,
|
|
* int maxNodex,
|
|
* DtTopicListStruct *pHead,
|
|
* DtTopicListStruct *pTale,
|
|
* totalNodes)
|
|
*
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value: Void.
|
|
*
|
|
* Purpose: Adds an element to the top of the given topicList.
|
|
*
|
|
*****************************************************************************/
|
|
void _DtHelpTopicListAddToHead(
|
|
char *locationId,
|
|
XmString topicTitle,
|
|
int topicType,
|
|
int maxNodes,
|
|
char *accessPath,
|
|
DtTopicListStruct **pHead,
|
|
DtTopicListStruct **pTale,
|
|
int *totalNodes,
|
|
int scrollPosition)
|
|
{
|
|
DtTopicListStruct *pTemp=NULL;
|
|
|
|
/* add the new topic to the top */
|
|
pTemp = (DtTopicListStruct *) XtMalloc((sizeof(DtTopicListStruct)));
|
|
|
|
pTemp->pNext = (*pHead);
|
|
pTemp->pPrevious = NULL;
|
|
|
|
/* Assign the passed in values to our first element */
|
|
pTemp->locationId = XtNewString(locationId);
|
|
pTemp->topicTitleLbl = NULL;
|
|
if (topicTitle != NULL)
|
|
pTemp->topicTitleLbl= XmStringCopy(topicTitle);
|
|
pTemp->topicType = topicType;
|
|
pTemp->helpVolume = XtNewString(accessPath);
|
|
pTemp->scrollPosition = scrollPosition;
|
|
|
|
/* Add locationId as first element if pHead = NULL */
|
|
if (*pHead == NULL)
|
|
{
|
|
/* Assign our tale pointer */
|
|
(*pTale) = (*pHead);
|
|
|
|
/* Make sure or totalNodes counter is correct, e.g. force it to 1 */
|
|
*totalNodes = 1;
|
|
}
|
|
else
|
|
{ /* We have a list so add the new topic to the top */
|
|
|
|
(*pHead)->pPrevious = pTemp;
|
|
|
|
/* Assign our tale pointer only the first time in this block */
|
|
if (*totalNodes == 1)
|
|
(*pTale) = (*pHead);
|
|
|
|
/* Re-Assign our head pointer to point to the new head of the list */
|
|
|
|
/* Bump our totalNode count */
|
|
*totalNodes = *totalNodes +1;
|
|
}
|
|
|
|
/* set the head to the current entry */
|
|
(*pHead) = pTemp;
|
|
|
|
/* If we have reached our maxNodes remove a node from the end of our list */
|
|
if (*totalNodes > maxNodes)
|
|
{
|
|
pTemp = (*pTale);
|
|
(*pTale) = (*pTale)->pPrevious;
|
|
(*pTale)->pNext = NULL;
|
|
pTemp->pPrevious = NULL;
|
|
|
|
/* Free the id String and AccessPath elements */
|
|
XtFree(pTemp->locationId);
|
|
XtFree(pTemp->helpVolume);
|
|
if (pTemp->topicTitleLbl != NULL)
|
|
XmStringFree(pTemp->topicTitleLbl);
|
|
|
|
/* Now, free the whole node */
|
|
XtFree((char*)pTemp);
|
|
|
|
/* Bump back our total node counter */
|
|
*totalNodes = *totalNodes -1;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpTopicListDeleteHead(
|
|
* DtTopicListStruct *pHead,
|
|
* DtTopicListStruct *pTale,
|
|
* totalNodes)
|
|
*
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value: Void.
|
|
*
|
|
* Purpose: Delets an element from the top of the given topicList.
|
|
*
|
|
*****************************************************************************/
|
|
void _DtHelpTopicListDeleteHead(
|
|
DtTopicListStruct **pHead,
|
|
DtTopicListStruct **pTale,
|
|
int *totalNodes)
|
|
{
|
|
DtTopicListStruct *pTemp=NULL;
|
|
|
|
/* Delete the top node in our topic list */
|
|
if (*pHead != NULL)
|
|
{
|
|
pTemp = (*pHead);
|
|
if(pTemp != (*pTale)) /* (e.g. more than one node in list) */
|
|
{
|
|
(*pHead) = pTemp->pNext;
|
|
pTemp->pNext = NULL;
|
|
(*pHead)->pPrevious = NULL;
|
|
|
|
/* Free the id String and accessPath elements */
|
|
XtFree(pTemp->locationId);
|
|
XtFree(pTemp->helpVolume);
|
|
|
|
/* Now, free the whole node */
|
|
XtFree((char*)pTemp);
|
|
|
|
/* Bump back our total node counter */
|
|
*totalNodes = *totalNodes -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpMapCB()
|
|
*
|
|
*
|
|
*
|
|
* Parameters: client_data is the widget in reference to
|
|
* which widget w is placed
|
|
*
|
|
* Return Value: Void.
|
|
*
|
|
* Purpose: Determins where a new child dialog should be mapped in
|
|
* relation to its parent.
|
|
*
|
|
* Algorithm: 1. attempt left or right placement with no overlap
|
|
* 2. if fails, attempt up or down placement with no overlap
|
|
* 3. if fails, determines location with least
|
|
* amount of overlap, and places there.
|
|
*
|
|
*****************************************************************************/
|
|
XtCallbackProc _DtHelpMapCB(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data )
|
|
{
|
|
Arg args[2];
|
|
Widget parent;
|
|
Position centeredY, bestX, bestY, pX, pY;
|
|
Dimension pHeight, myHeight, pWidth, myWidth;
|
|
Dimension maxX, maxY;
|
|
int rhsX, lhsX, topY, botY; /* needs to be int, not Dimension */
|
|
Display * display;
|
|
Screen * screen;
|
|
int screenNumber;
|
|
|
|
parent = (Widget)client_data;
|
|
display = XtDisplay(w);
|
|
screen = XtScreen(w);
|
|
screenNumber = XScreenNumberOfScreen(screen);
|
|
|
|
pX = XtX(parent);
|
|
pY = XtY(parent);
|
|
if (pX < 0) pX = 0;
|
|
if (pY < 0) pY = 0;
|
|
pHeight = XtHeight(parent);
|
|
pWidth = XtWidth(parent);
|
|
myHeight = XtHeight(w);
|
|
myWidth = XtWidth(w);
|
|
maxX = XDisplayWidth(display,screenNumber);
|
|
maxY = XDisplayHeight(display,screenNumber);
|
|
|
|
/* algorithm
|
|
* 1. attempt left or right placement with no overlap
|
|
* 2. if fails, attempt up or down placement with no overlap
|
|
* 3. if fails, places on the right in the middle
|
|
*/
|
|
|
|
/* first try left right placement */
|
|
bestY = pY + pHeight/2 - myHeight/2;
|
|
centeredY = bestY;
|
|
rhsX = pX + pWidth;
|
|
lhsX = pX - myWidth - 8; /* 8: account for border */
|
|
if ( ((int)(rhsX + myWidth)) < ((int) maxX) ) bestX = rhsX;
|
|
else if ( lhsX > 0 ) bestX = lhsX;
|
|
else
|
|
{
|
|
/* then try up down placement */
|
|
bestX = pX + pWidth/2 - myWidth/2;
|
|
botY = pY + pHeight;
|
|
topY = pY - myHeight - 44; /* 44: account for menu border */
|
|
if ( ((int)(botY + myWidth)) < ((int) maxY) ) bestY = botY;
|
|
else if ( topY > 0 ) bestY = topY;
|
|
else
|
|
{
|
|
/* otherwise, center vertically and on the right */
|
|
bestX = maxX - myWidth;
|
|
bestY = centeredY;
|
|
}
|
|
}
|
|
|
|
XtSetArg(args[0], XmNx, bestX);
|
|
XtSetArg(args[1], XmNy, bestY);
|
|
XtSetValues(w, args, 2);
|
|
|
|
return((XtCallbackProc) NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpMapCenteredCB(
|
|
*
|
|
*
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value: Void.
|
|
*
|
|
* Purpose: Determins where the center of our help dialog is and sets
|
|
* where new child dialog should be mapped such that its centered.
|
|
*
|
|
*****************************************************************************/
|
|
XtCallbackProc _DtHelpMapCenteredCB(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data )
|
|
{
|
|
Arg args[2];
|
|
Widget parent;
|
|
Position newX, newY, pY, pX;
|
|
Dimension pHeight, myHeight, pWidth, myWidth;
|
|
|
|
parent = (Widget)client_data;
|
|
|
|
pX = XtX(parent);
|
|
pY = XtY(parent);
|
|
pHeight = XtHeight(parent);
|
|
pWidth = XtWidth(parent);
|
|
myHeight = XtHeight(w);
|
|
myWidth = XtWidth(w);
|
|
|
|
|
|
newY = ((Position)(pHeight - myHeight) / 2) + pY;
|
|
newX = ((Position)(pWidth - myWidth) / 2) + pX;
|
|
|
|
XtSetArg(args[0], XmNx, newX);
|
|
XtSetArg(args[1], XmNy, newY);
|
|
XtSetValues(w, args, 2);
|
|
|
|
return((XtCallbackProc) NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpDisplayDefinitionBox(
|
|
* Widget new,
|
|
* Widget definitionBox,
|
|
* char * path,
|
|
* char * locationId);
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: This routine will create and post the definition box.
|
|
* (e.g. the Quick Help Dialog widget)
|
|
*
|
|
****************************************************************************/
|
|
void _DtHelpDisplayDefinitionBox(
|
|
Widget parent,
|
|
Widget **definitionBox,
|
|
char * path,
|
|
char * locationId)
|
|
{
|
|
|
|
Arg args[10];
|
|
int n;
|
|
Widget printWidget, helpWidget, backWidget;
|
|
XmString closeString;
|
|
char *title;
|
|
|
|
/* get the title from the main help dialog and use it here for */
|
|
n = 0;
|
|
XtSetArg (args[n], XmNtitle, &(title)); ++n;
|
|
XtGetValues (XtParent(parent), args, n);
|
|
|
|
|
|
if (*definitionBox == NULL)
|
|
{
|
|
|
|
/* Create the QuickHelpDialog widget to use as the definition box */
|
|
closeString = XmStringCreateLocalized(((char *)_DTGETMESSAGE
|
|
(HUSET, 2,"Close")));
|
|
n =0;
|
|
XtSetArg (args[n], DtNhelpVolume, path); n++;
|
|
XtSetArg (args[n], DtNhelpType, DtHELP_TYPE_TOPIC); n++;
|
|
XtSetArg (args[n], DtNlocationId, locationId); n++;
|
|
XtSetArg (args[n], DtNcloseLabelString, closeString); n++;
|
|
XtSetArg (args[n], XmNtitle, title); n++;
|
|
*definitionBox =
|
|
(Widget *)DtCreateHelpQuickDialog(parent, "definitionBox",
|
|
args, n);
|
|
XmStringFree(closeString);
|
|
|
|
/* Catch the close callback so we can destroy the widget */
|
|
XtAddCallback((Widget)*definitionBox, DtNcloseCallback,
|
|
CloseDefBoxCB, (XtPointer) NULL);
|
|
|
|
|
|
/* We do not want a print button for now so we unmap it */
|
|
printWidget = DtHelpQuickDialogGetChild ((Widget)*definitionBox,
|
|
DtHELP_QUICK_PRINT_BUTTON);
|
|
XtUnmanageChild (printWidget);
|
|
|
|
/* We do not want a help button for now so we unmap it */
|
|
helpWidget = DtHelpQuickDialogGetChild ((Widget)*definitionBox,
|
|
DtHELP_QUICK_HELP_BUTTON);
|
|
XtUnmanageChild (helpWidget);
|
|
|
|
|
|
/* We do not want a BACK button for now so we unmap it */
|
|
backWidget = DtHelpQuickDialogGetChild ((Widget)*definitionBox,
|
|
DtHELP_QUICK_BACK_BUTTON);
|
|
XtUnmanageChild (backWidget);
|
|
|
|
|
|
|
|
/* Adjust the decorations for the dialog shell of the dialog */
|
|
n = 0;
|
|
XtSetArg(args[n], XmNmwmFunctions, MWM_FUNC_RESIZE | MWM_FUNC_MOVE); n++;
|
|
XtSetArg (args[n], XmNmwmDecorations,
|
|
MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_RESIZEH); n++;
|
|
XtSetValues (XtParent(*definitionBox), args, n);
|
|
|
|
/* Add the popup position callback to our history dialog */
|
|
XtAddCallback (XtParent(*definitionBox), XmNpopupCallback,
|
|
(XtCallbackProc)_DtHelpMapCenteredCB, (XtPointer)XtParent(parent));
|
|
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
/* We already have one so lets use it. */
|
|
|
|
/* Set the proper title */
|
|
n = 0;
|
|
XtSetArg (args[n], XmNtitle, title); ++n;
|
|
XtSetValues (XtParent(*definitionBox), args, n);
|
|
|
|
/* Set the proper contents. */
|
|
n = 0;
|
|
XtSetArg (args[n], DtNhelpType, DtHELP_TYPE_TOPIC); n++;
|
|
XtSetArg (args[n], DtNhelpVolume, path); n++;
|
|
XtSetArg (args[n], DtNlocationId, locationId); n++;
|
|
XtSetValues ((Widget)*definitionBox, args, n);
|
|
|
|
}
|
|
|
|
|
|
/* Display the dialog */
|
|
XtManageChild((Widget)*definitionBox);
|
|
XtMapWidget(XtParent((Widget)*definitionBox));
|
|
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: static void CloseDefBoxCB(
|
|
* Widget w,
|
|
* XtPointer client_data,
|
|
* XtPointer call_data);
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: This routine closes and destroys the Definition Box
|
|
* Dialog Widget that we create.
|
|
*
|
|
****************************************************************************/
|
|
static void CloseDefBoxCB(
|
|
Widget w,
|
|
XtPointer client_data,
|
|
XtPointer call_data )
|
|
{
|
|
|
|
XtUnmanageChild(w);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpDisplayFormatError()
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: This routine generate and display the proper errror
|
|
* message to the display area as well as send the proper
|
|
* error to XmWarning() function.
|
|
*
|
|
****************************************************************************/
|
|
void _DtHelpDisplayFormatError(
|
|
XtPointer displayArea,
|
|
Widget widget,
|
|
char *userError,
|
|
char *systemError)
|
|
{
|
|
XtPointer topicHandle;
|
|
|
|
/* Set the string to the current help dialog */
|
|
(void) _DtHelpFormatAsciiStringDynamic(displayArea, userError, &topicHandle);
|
|
|
|
/* We ignor the status return here, because if we error out here we are
|
|
* in big trouble because this is an error routine
|
|
*/
|
|
|
|
_DtHelpDisplayAreaSetList (displayArea, topicHandle, FALSE, -1);
|
|
|
|
if (systemError != NULL)
|
|
XmeWarning((Widget)widget, systemError);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpCommonHelpInit()
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: This routine inits common help stuff
|
|
*
|
|
****************************************************************************/
|
|
void _DtHelpCommonHelpInit(
|
|
_DtHelpCommonHelpStuff * help)
|
|
{
|
|
help->topLevelId = NULL;
|
|
help->currentHelpFile = NULL;
|
|
|
|
/* for help on help */
|
|
if ( help->helpOnHelpVolume != _DtHelpDefaultHelp4HelpVolume)
|
|
help->helpOnHelpVolume = XtNewString(help->helpOnHelpVolume);
|
|
if ( NULL == help->helpOnHelpVolume )
|
|
help->helpOnHelpVolume = (char *)_DtHelpDefaultHelp4HelpVolume;
|
|
help->pHelpListHead = NULL; /* Help List Pointer */
|
|
help->onHelpDialog = NULL; /* help on help dialog */
|
|
help->pDisplayArea = NULL; /* Display widget handle */
|
|
|
|
/* get the search paths used by the widget */
|
|
help->userVolumeSearchPath = _DtHelpGetUserSearchPath();
|
|
help->sysVolumeSearchPath = _DtHelpGetSystemSearchPath();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpCommonHelpClean()
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: This routine cleans up common help stuff
|
|
*
|
|
****************************************************************************/
|
|
void _DtHelpCommonHelpClean(
|
|
_DtHelpCommonHelpStuff * help,
|
|
Boolean destroy)
|
|
{
|
|
free(help->topLevelId);
|
|
XtFree(help->currentHelpFile);
|
|
|
|
help->topLevelId = NULL;
|
|
help->currentHelpFile = NULL;
|
|
|
|
if (destroy)
|
|
{
|
|
if (help->helpOnHelpVolume != _DtHelpDefaultHelp4HelpVolume)
|
|
XtFree(help->helpOnHelpVolume);
|
|
|
|
/* Free all the info we saved for our help callbacks */
|
|
_DtHelpListFree(&help->pHelpListHead);
|
|
|
|
XtFree(help->userVolumeSearchPath);
|
|
XtFree(help->sysVolumeSearchPath);
|
|
|
|
memset(help,0,sizeof(_DtHelpCommonHelpStuff));
|
|
}
|
|
else
|
|
{
|
|
/* Set our display area to a null starting vlaues */
|
|
_DtHelpDisplayAreaClean(help->pDisplayArea);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: void _DtHelpSetDlgButtonsWidth
|
|
*
|
|
* Parameters:
|
|
*
|
|
* Return Value:
|
|
*
|
|
* Purpose: This routine cleans up common help stuff
|
|
*
|
|
****************************************************************************/
|
|
void _DtHelpSetButtonPositions(
|
|
Widget btnList[],
|
|
int numBtns,
|
|
Dimension minFormWidth,
|
|
Dimension btnMargins,
|
|
Dimension minBetweenBtnSpace)
|
|
{ /* position buttons */
|
|
|
|
/* This code adds up the sizes of the buttons to go into
|
|
the bottom row and calculates the form position percentages.
|
|
All buttons are the same size, and are able to hold all
|
|
the strings that may be dynamically placed in them.
|
|
All buttons are 5% apart. */
|
|
/* This code is specifically written to handle 3 buttons
|
|
and assumes that the first 3 strings are to the ActionBtn */
|
|
Dimension minWidthWithSpace = 0;
|
|
Dimension borderWidth = 0;
|
|
Dimension sumWidth = 0;
|
|
Dimension leftPos = 0;
|
|
Dimension rightPos = 0;
|
|
Dimension spaceWidth = 0;
|
|
Dimension btnWidth;
|
|
Dimension maxBtnWidth = 0;
|
|
float scale = 0.0;
|
|
XmFontList fontList = NULL;
|
|
int i;
|
|
int n;
|
|
Arg args[5];
|
|
|
|
if (numBtns <= 0 || NULL == btnList[0]) return;
|
|
|
|
/* get the fontList for the button */
|
|
XtSetArg (args[0], XmNborderWidth, &borderWidth);
|
|
XtSetArg (args[1], XmNfontList, &fontList);
|
|
XtGetValues (btnList[0], args, 2);
|
|
/* assumption: the fontList that is returned is not owned by me; don't free */
|
|
|
|
/* cycle through all buttons */
|
|
for (i=0; i<numBtns && NULL!=btnList[i]; i++)
|
|
{
|
|
XmString labelString;
|
|
|
|
/* get the label from the button */
|
|
XtSetArg (args[0], XmNlabelString, &labelString);
|
|
XtGetValues (btnList[i], args, 1);
|
|
|
|
btnWidth = XmStringWidth(fontList,labelString) + borderWidth + btnMargins;
|
|
if (btnWidth > maxBtnWidth) maxBtnWidth = btnWidth;
|
|
|
|
XmStringFree(labelString);
|
|
} /* for calcing widths */
|
|
numBtns = i; /* number of valid buttons */
|
|
|
|
/* calc the space */
|
|
sumWidth = maxBtnWidth * numBtns;
|
|
minWidthWithSpace = sumWidth + minBetweenBtnSpace * (numBtns * 2);
|
|
if (minWidthWithSpace > minWidthWithSpace) minFormWidth = minWidthWithSpace;
|
|
spaceWidth = ((int)(minFormWidth - sumWidth) / (numBtns * 2));
|
|
|
|
/* scale pixels to percent */
|
|
scale = 100.0 / (float) minFormWidth;
|
|
|
|
/* set the positions of each button */
|
|
leftPos = spaceWidth;
|
|
for ( i=0; i<numBtns; i++ )
|
|
{
|
|
rightPos = leftPos + maxBtnWidth;
|
|
|
|
n = 0;
|
|
XtSetArg (args[n], XmNleftAttachment, XmATTACH_POSITION); n++;
|
|
XtSetArg (args[n], XmNleftPosition, (Dimension) (((float) leftPos)*scale)); n++;
|
|
XtSetArg (args[n], XmNrightAttachment, XmATTACH_POSITION); n++;
|
|
XtSetArg (args[n], XmNrightPosition,(Dimension) (((float) rightPos)*scale)); n++;
|
|
XtSetValues (btnList[i], args, n);
|
|
|
|
leftPos = rightPos + spaceWidth + spaceWidth;
|
|
} /* setup the positions for all buttons */
|
|
} /* _DtHelpSetDlgButtonsWidth */
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Function: _DtHelpXmFontListGetPropertyMax
|
|
*
|
|
* Parameters:
|
|
* fontList: an XmFontList
|
|
* atom: an XA_xxx value (see Vol 1, chpt 6.2.9)
|
|
* ret_propertyValue: ptr to long value that will hold the max value
|
|
*
|
|
* Return Value:
|
|
* True: got at least one value
|
|
* False: unable to get any value; ret_propertyValue unchanged
|
|
*
|
|
* Purpose:
|
|
* This function returns the max value of XGetFontProperty calls
|
|
* for each font in the XmFontList
|
|
* If there is an error, ret_propertyValue is left unchanged.
|
|
*
|
|
****************************************************************************/
|
|
Boolean
|
|
_DtHelpXmFontListGetPropertyMax(
|
|
XmFontList fontList,
|
|
Atom atom,
|
|
unsigned long *ret_propertyValue)
|
|
{
|
|
Boolean gotValue = False;
|
|
XmFontContext context;
|
|
XmFontListEntry entry;
|
|
XtPointer fontData;
|
|
|
|
if (NULL == fontList) return False; /* RETURN on error */
|
|
|
|
/* walk through the fontList entries and add them in */
|
|
XmFontListInitFontContext(&context,fontList);
|
|
for ( entry = XmFontListNextEntry(context);
|
|
NULL != entry;
|
|
entry = XmFontListNextEntry(context) )
|
|
{
|
|
unsigned long value;
|
|
XmFontType type;
|
|
|
|
fontData = XmFontListEntryGetFont(entry,&type);
|
|
if (XmFONT_IS_FONT == type)
|
|
{
|
|
XFontStruct * fontStruct;
|
|
|
|
/* cast according to type */
|
|
fontStruct = (XFontStruct *) fontData;
|
|
|
|
if(XGetFontProperty(fontStruct, atom, &value) == TRUE)
|
|
{
|
|
if(gotValue == False) /* haven't gotten any prop value yet */
|
|
{
|
|
*ret_propertyValue = value;
|
|
gotValue = True;
|
|
}
|
|
else /* have a good prop value already...get the max one */
|
|
{
|
|
if(value > *ret_propertyValue)
|
|
*ret_propertyValue = value;
|
|
}
|
|
} /* if the getproperty call was good */
|
|
}
|
|
else /* XmFONT_IS_FONTSET */
|
|
{
|
|
XFontSet fontSet;
|
|
XFontStruct **font_list;
|
|
char **name_list;
|
|
int numfont;
|
|
int i;
|
|
|
|
/* cast according to type */
|
|
fontSet = (XFontSet) fontData;
|
|
|
|
numfont=XFontsOfFontSet(fontSet,&font_list,&name_list);
|
|
for(i = 0; i < numfont; i++)
|
|
{
|
|
if(XGetFontProperty(font_list[i], atom, &value) == TRUE)
|
|
{
|
|
if(gotValue == False) /* haven't gotten any prop value yet */
|
|
{
|
|
*ret_propertyValue = value;
|
|
gotValue = True;
|
|
}
|
|
else /* have a good prop value already...get the max one */
|
|
{
|
|
if(value > *ret_propertyValue)
|
|
*ret_propertyValue = value;
|
|
}
|
|
} /* if the getproperty call was good */
|
|
} /* for all fonts in the font set */
|
|
} /* this entry uses a font set */
|
|
} /* for all font entries in the font list */
|
|
XmFontListFreeFontContext(context);
|
|
|
|
return(gotValue);
|
|
}
|