/****************************************************************************/
/* WebWorks Help API Platform Functions                                     */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

/*$Id: wwhunix.c,v 1.14 2004/12/17 19:16:54 allums Exp $*/


#ifdef __hpux
#define _POSIX_SOURCE 1
#endif

#include "wwhelp/wwhplat.h"

#include "wwhelp/wwhurl.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/WinUtil.h>


/****************************************************************************/
/* Mozilla X atom names                                                     */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

#define WWHELP_MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
#define WWHELP_MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
#define WWHELP_MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
#define WWHELP_MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"


/****************************************************************************/
/* Platform specific datatypes, variables, and function prototypes          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

#ifdef __cplusplus
extern "C"
{
#endif

#define WWHELP_COMMAND_MAXARGUMENTS    16
#define WWHELP_PROGRAMARRAY_MAXLENGTH  64
#define WWHELP_COMMAND_MAXLENGTH       1024
#define WWHELP_URL_MAXLENGTH           4096

int  private_WWHelp_Platform_FileIsExecutable(const char* pParamProgram);

int  private_WWHelp_Platform_Command(const int   bParamWait,
                                     const char* pParamProgram,
                                                 ...);

int  private_WWHelp_Platform_SetProcessSignature(unsigned char* pParamProcessSignature);

int  private_WWHelp_Platform_WindowPropertyExists(Display* pParamDisplay,
                                                  Window   ParamWindow,
                                                  Atom     ParamAtom);

int  private_WWHelp_Platform_WindowPropertyGet(      Display*       pParamDisplay,
                                                     Window         ParamWindow,
                                                     Atom           ParamAtom,
                                                     unsigned char* pParamBuffer,
                                               const unsigned int   ParamBufferLength);

int  private_WWHelp_Platform_WindowPropertyCreate(      Display*       pParamDisplay,
                                                        Window         ParamWindow,
                                                        Atom           ParamAtom,
                                                  const unsigned char* pParamData);


int  private_WWHelp_Platform_WindowPropertyDestroy(Display* pParamDisplay,
                                                   Window   ParamWindow,
                                                   Atom     ParamAtom);

int  private_WWHelp_Platform_WaitForPropertyDelete(Display* pParamDisplay,
                                                   Window   ParamWindow,
                                                   Atom     ParamAtom);

Window  private_WWHelp_Platform_FindMozillaWindow(Display* pParamDisplay,
                                                  Atom     ParamVersionAtom);

int  private_WWHelp_Platform_LockMozillaWindow(      Display*       pParamDisplay,
                                                     Window         ParamMozillaWindow,
                                                     Atom           ParamLockAtom,
                                               const unsigned char* pParamProcessSignature);

int  private_WWHelp_Platform_UnlockMozillaWindow(      Display*       pParamDisplay,
                                                       Window         ParamMozillaWindow,
                                                       Atom           ParamLockAtom,
                                                 const unsigned char* pParamProcessSignature);

int  private_WWHelp_Platform_SendMozillaRemoteCommand(      Display*       pParamDisplay,
                                                            Window         ParamMozillaWindow,
                                                            Atom           ParamCommandAtom,
                                                            Atom           ParamResponseAtom,
                                                      const unsigned char* pParamProcessSignature,
                                                      const unsigned char* pParamCommand);

#ifdef __cplusplus
}
#endif


/****************************************************************************/
/* private_WWHelp_Platform_FileIsExecutable                                 */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_FileIsExecutable(const char* pParamFileName)
{
  int          VarReturnCode = -1;
  int          VarResult;
  struct stat  VarStat;


  VarResult = stat(pParamFileName, &VarStat);
  if (VarResult == 0)
  {
    if (VarStat.st_mode | S_IXOTH)
    {
      /* Success! */
      /*          */
      VarReturnCode = 0;
    }
  }

  return VarReturnCode;
}

/****************************************************************************/
/* private_WWHelp_Platform_Command                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_Command(const int   bParamWait,
                                const char* pParamProgram,
                                            ...)
{
  int      ReturnCode = -1;
  char     pStdErrFileName[256];
  int      StdErrFileDescriptor;
  pid_t    ChildProcessID;
  pid_t    WaitingProcessID;
  int      ChildProcessReturnStatus;
  va_list  ParamArguments;
  char*    Arguments[WWHELP_COMMAND_MAXARGUMENTS];
  int      Index;
  char*    pArgument;
  FILE*    pStdErrFile;


  /* Create temporary file to redirect stderr to */
  /*                                             */
  strcpy(pStdErrFileName, "/tmp/wwhapiXXXXXX");
  StdErrFileDescriptor = mkstemp(pStdErrFileName);
  if (StdErrFileDescriptor != -1)
  {
    /* Unique file name created, now close the file for now */
    /*                                                      */
    close(StdErrFileDescriptor);

    /* Fork! */
    /*       */
    ChildProcessID = fork();
    if (ChildProcessID >= 0)
    {
      if (ChildProcessID > 0)
      {
        if (bParamWait)
        {
          /* Parent process, wait for child to end */
          /*                                       */
          WaitingProcessID = waitpid(ChildProcessID, &ChildProcessReturnStatus, 0);
          if (WaitingProcessID == ChildProcessID)
          {
            /* Confirm program exited normally */
            /*                                 */
            if (WIFEXITED(ChildProcessReturnStatus) > 0)
            {
              /* Get return status */
              /*                   */
              if (WEXITSTATUS(ChildProcessReturnStatus) == 0)
              {
                /* Check stderr for messages */
                /*                           */
                pStdErrFile = fopen(pStdErrFileName, "r");
                if (pStdErrFile != NULL)
                {
                  /* Check for file length greater than 0 */
                  /*                                      */
                  if ((fseek(pStdErrFile, 0, SEEK_END) == 0) &&
                      (ftell(pStdErrFile) == 0))
                  {
                    /* Success! */
                    /*          */
                    ReturnCode = 0;
                  }

                  /* Close and delete the file */
                  /*                           */
                  fclose(pStdErrFile);
                  remove(pStdErrFileName);
                }
              }
            }
          }
        }
        else
        {
          /* Success! */
          /*          */
          ReturnCode = 0;
        }
      }
      else
      {
        /* Child process, exec the given command */
        /*                                       */

        /* Copy variable argument pointers to an array */
        /*                                             */
        va_start(ParamArguments, pParamProgram);
        Index = 0;
        pArgument = (char*)pParamProgram;
        while (pArgument != NULL)
        {
          Arguments[Index] = pArgument;

          pArgument = va_arg(ParamArguments, char*);

          Index++;
        }
        va_end(ParamArguments);

        /* NULL terminate argument array */
        /*                               */
        Arguments[Index] = NULL;

        /* Redirect stdout and stderr to /dev/null */
        /*                                         */
        if ((freopen("/dev/null", "a", stdout) != NULL) &&
            (freopen(pStdErrFileName, "w", stderr) != NULL))
        {
          /* Execute the command */
          /*                     */
          execvp(pParamProgram, Arguments);
        }

        /* exec failed or stdout and stderr not redirected */
        /*                                                 */
        exit(-1);
      }
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_SetProcessSignature                              */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_SetProcessSignature(unsigned char* pParamProcessSignature)
{
  int   VarReturnCode = -1;
  int   VarPrefixLength;
  int   VarMaxHostNameLength;
  char* pVarHostName;


  /* Determine process signature */
  /*                             */
  sprintf((char*)pParamProcessSignature, "pid%d@", getpid());
  VarPrefixLength = strlen((const char*)pParamProcessSignature);
  VarMaxHostNameLength = WWHELP_PROCESSSIGNATURE_MAXLENGTH - VarPrefixLength - 1;
  pVarHostName = ((char*)pParamProcessSignature) + VarPrefixLength;
  VarReturnCode = gethostname(pVarHostName, VarMaxHostNameLength);

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_WindowPropertyExists                             */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_WindowPropertyExists(Display* pParamDisplay,
                                             Window   ParamWindow,
                                             Atom     ParamAtom)
{
  int            VarReturnCode = -1;
  int            VarResult;
  Atom           VarType;
  int            VarFormat;
  unsigned long  VarNumberOfItems;
  unsigned long  VarBytesAfter;
  unsigned char* pVarData;


  /* Get current window property value */
  /*                                   */
  VarResult = XGetWindowProperty(pParamDisplay, ParamWindow, ParamAtom,
                                 0, (65536 / sizeof (long)),
                                 False, /* don't delete */
                                 XA_STRING,
                                 &VarType, &VarFormat,
                                 &VarNumberOfItems, &VarBytesAfter,
                                 &pVarData);
  if (VarResult == Success)
  {
    if (VarType != None)
    {
      /* Success! */
      /*          */
      VarReturnCode = 0;
    }

    /* Release data string */
    /*                     */
    if (pVarData)
    {
      XFree(pVarData);
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_WindowPropertyGet                                */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_WindowPropertyGet(      Display*       pParamDisplay,
                                                Window         ParamWindow,
                                                Atom           ParamAtom,
                                                unsigned char* pParamBuffer,
                                          const unsigned int   ParamBufferLength)
{
  int            VarReturnCode = -1;
  int            VarResult;
  Atom           VarType;
  int            VarFormat;
  unsigned long  VarNumberOfItems;
  unsigned long  VarBytesAfter;
  unsigned char* pVarData;


  /* Get current window property value */
  /*                                   */
  VarResult = XGetWindowProperty(pParamDisplay, ParamWindow, ParamAtom,
                                 0, (65536 / sizeof (long)),
                                 False, /* don't delete */
                                 XA_STRING,
                                 &VarType, &VarFormat,
                                 &VarNumberOfItems, &VarBytesAfter,
                                 &pVarData);
  if (VarResult == Success)
  {
    /* Check data */
    /*            */
    if ((VarType != None) &&
        (pVarData))
    {
      if (ParamBufferLength > 1)
      {
        /* Copy the data into the provided buffer */
        /*                                        */
        strncpy((char*)pParamBuffer, (char*)pVarData, ParamBufferLength - 1);

        /* Success! */
        /*          */
        VarReturnCode = 0;
      }
    }

    /* Release data string */
    /*                     */
    if (pVarData)
    {
      XFree(pVarData);
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_WindowPropertyCreate                             */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_WindowPropertyCreate(      Display*       pParamDisplay,
                                                   Window         ParamWindow,
                                                   Atom           ParamAtom,
                                             const unsigned char* pParamData)
{
  int  VarReturnCode = -1;
  int  VarResult;


  /* Property exists? */
  /*                  */
  VarResult = private_WWHelp_Platform_WindowPropertyExists(pParamDisplay,
                                                           ParamWindow,
                                                           ParamAtom);
  if (VarResult != 0)
  {
    /* Property does not exist - create it */
    /*                                     */
    XChangeProperty(pParamDisplay, ParamWindow, ParamAtom,
                    XA_STRING, 8, PropModeReplace,
                    (unsigned char*)pParamData, strlen((const char*)pParamData));

    /* Success! */
    /*          */
    VarReturnCode = 0;
  }

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_WindowPropertyDestroy                            */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_WindowPropertyDestroy(Display* pParamDisplay,
                                              Window   ParamWindow,
                                              Atom     ParamAtom)
{
  int            VarReturnCode = -1;
  int            VarResult;
  Atom           VarType;
  int            VarFormat;
  unsigned long  VarNumberOfItems;
  unsigned long  VarBytesAfter;
  unsigned char* pVarData;


  VarResult = XGetWindowProperty(pParamDisplay, ParamWindow, ParamAtom,
                                 0, (65536 / sizeof (long)),
                                 True, /* atomic delete after */
                                 XA_STRING,
                                 &VarType, &VarFormat,
                                 &VarNumberOfItems, &VarBytesAfter,
                                 &pVarData);
  if (VarResult == Success)
  {
    /* Success! */
    /*          */
    VarReturnCode = 0;

    /* Release data string */
    /*                     */
    if (pVarData)
    {
      XFree(pVarData);
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_WaitForPropertyDelete                            */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_WaitForPropertyDelete(Display* pParamDisplay,
                                              Window   ParamWindow,
                                              Atom     ParamAtom)
{
  int     VarReturnCode = -1;
  int     bVarDone;
  XEvent  VarEvent;


  /* Register to receive events */
  /*                            */
  XSelectInput(pParamDisplay, ParamWindow, PropertyChangeMask | StructureNotifyMask);

  /* Wait for a PropertyDelete event or a DestroyNotify event on the window */
  /*                                                                        */
  bVarDone = 0;
  while ( ! bVarDone)
  {
    /* Get the next event */
    /*                    */
    XNextEvent(pParamDisplay, &VarEvent);

    /* Check event type */
    /*                  */
    if ((VarEvent.xany.type == DestroyNotify) &&
        (VarEvent.xdestroywindow.window == ParamWindow))
    {
      /* Window destroyed */
      /*                  */
      bVarDone = 1;
    }
    else if ((VarEvent.xany.type == PropertyNotify) &&
             (VarEvent.xproperty.window == ParamWindow) &&
             (VarEvent.xproperty.state == PropertyDelete) &&
             (VarEvent.xproperty.atom == ParamAtom))
    {
      /* Property was deleted */
      /*                      */
      bVarDone = 1;

      /* Success! */
      /*          */
      VarReturnCode = 0;
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_FindMozillaWindow                                */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

Window
private_WWHelp_Platform_FindMozillaWindow(Display* pParamDisplay,
                                          Atom     ParamVersionAtom)
{
  Window        VarMozillaWindow = 0;
  Window        VarRootWindow;
  Status        VarStatus;
  Window        VarQueriedRootWindow;
  Window        VarParentWindow;
  Window*       VarChildArray;
  unsigned int  VarChildArrayLength;
  int           VarIndex;
  Window        VarWindow;
  int           VarResult;


  /* Access the list of windows on this display */
  /*                                            */
  VarRootWindow = RootWindowOfScreen(DefaultScreenOfDisplay(pParamDisplay));
  VarStatus = XQueryTree(pParamDisplay, VarRootWindow,
                         &VarQueriedRootWindow, &VarParentWindow,
                         &VarChildArray, &VarChildArrayLength);
  if (VarStatus)
  {
    /* Confirm the list of windows exists and used the display root window */
    /*                                                                     */
    if ((VarQueriedRootWindow == VarRootWindow) &&
        ( ! VarParentWindow) &&
        (VarChildArray != NULL) &&
        (VarChildArrayLength > 0))
    {
      /* Iterate the list of child windows, looking for Mozilla signatures */
      /*                                                                   */
      VarIndex = 0;
      while (( ! VarMozillaWindow) &&
             (VarIndex < VarChildArrayLength))
      {
        /* Access the child window */
        /*                         */
        VarWindow = XmuClientWindow(pParamDisplay, VarChildArray[VarIndex]);

        /* Check for Mozilla version atom */
        /*                                */
        VarResult = private_WWHelp_Platform_WindowPropertyExists(pParamDisplay, VarWindow, ParamVersionAtom);
        if (VarResult == 0)
        {
          /* Success! */
          /*          */
          VarMozillaWindow = VarWindow;
        }

        VarIndex++;
      }
    }
  }

  return VarMozillaWindow;
}


/****************************************************************************/
/* private_WWHelp_Platform_LockMozillaWindow                                */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_LockMozillaWindow(      Display*       pParamDisplay,
                                                Window         ParamMozillaWindow,
                                                Atom           ParamLockAtom,
                                          const unsigned char* pParamProcessSignature)
{
  int  VarReturnCode = -1;
  int  bVarDone;
  int  VarResult;


  /* Loop until window locked or destroyed */
  /*                                       */
  bVarDone = 0;
  while ( ! bVarDone)
  {
    /* Lock X server */
    /*               */
    XGrabServer(pParamDisplay);   /* ################################# DANGER! */

    /* Try to lock the window */
    /*                        */
    VarResult = private_WWHelp_Platform_WindowPropertyCreate(pParamDisplay,
                                                             ParamMozillaWindow,
                                                             ParamLockAtom,
                                                             pParamProcessSignature);
    /* Unlock X server */
    /*               */
    XUngrabServer(pParamDisplay); /* ################################# danger over */
    XSync(pParamDisplay, False);

    /* Property created? */
    /*                   */
    if (VarResult == 0)
    {
      bVarDone = 1;

      /* Success! */
      /*          */
      VarReturnCode = 0;
    }
    else
    {
      /* Window locked - wait for a PropertyDelete event and then retry */
      /*                                                                */
      VarResult = private_WWHelp_Platform_WaitForPropertyDelete(pParamDisplay,
                                                                ParamMozillaWindow,
                                                                ParamLockAtom);
      if (VarResult != 0)
      {
        /* Exit, window destroyed */
        /*                        */
        bVarDone = 1;
      }
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_UnlockMozillaWindow                              */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_UnlockMozillaWindow(      Display*       pParamDisplay,
                                                  Window         ParamMozillaWindow,
                                                  Atom           ParamLockAtom,
                                            const unsigned char* pParamProcessSignature)
{
  int            VarReturnCode = -1;
  int            VarResult;
  unsigned char  VarLockData[WWHELP_PROCESSSIGNATURE_MAXLENGTH];


  /* Get the current signature in the lock property */
  /*                                                */
  VarResult = private_WWHelp_Platform_WindowPropertyGet(pParamDisplay,
                                                        ParamMozillaWindow,
                                                        ParamLockAtom,
                                                        VarLockData,
                                                        WWHELP_PROCESSSIGNATURE_MAXLENGTH);
  if (VarResult == 0)
  {
    /* Compare our signature to the retrieved signature */
    /*                                                  */
    if (strcmp((const char*)VarLockData, (const char*)pParamProcessSignature) == 0)
    {
      /* Destroy the lock property */
      /*                           */
      VarResult = private_WWHelp_Platform_WindowPropertyDestroy(pParamDisplay,
                                                                ParamMozillaWindow,
                                                                ParamLockAtom);
      if (VarResult == 0)
      {
        /* Success! */
        /*          */
        VarReturnCode = 0;
      }
    }
  }
}


/****************************************************************************/
/* private_WWHelp_Platform_SendMozillaRemoteCommand                         */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_SendMozillaRemoteCommand(      Display*       pParamDisplay,
                                                       Window         ParamMozillaWindow,
                                                       Atom           ParamCommandAtom,
                                                       Atom           ParamResponseAtom,
                                                 const unsigned char* pParamProcessSignature,
                                                 const unsigned char* pParamCommand)
{
  int            VarReturnCode = -1;
  int            bVarDone;
  XEvent         VarEvent;
  int            VarResult;
  unsigned char  VarResponse[WWHELP_MOZILLARESPONSE_MAXLENGTH];


  /* Register to receive events */
  /*                            */
  XSelectInput(pParamDisplay, ParamMozillaWindow, PropertyChangeMask | StructureNotifyMask);

  /* Send the command to Mozilla */
  /*                             */
  XChangeProperty(pParamDisplay, ParamMozillaWindow, ParamCommandAtom,
                  XA_STRING, 8, PropModeReplace,
                  (unsigned char*)pParamCommand, strlen((const char*)pParamCommand));

  /* Check command response */
  /*                        */
  bVarDone = 0;
  while ( ! bVarDone)
  {
    /* Get the next event */
    /*                    */
    XNextEvent(pParamDisplay, &VarEvent);

    /* Check event type */
    /*                  */
    if ((VarEvent.xany.type == DestroyNotify) &&
        (VarEvent.xdestroywindow.window == ParamMozillaWindow))
    {
      /* Window destroyed */
      /*                  */
      bVarDone = 1;
    }
    else if ((VarEvent.xany.type == PropertyNotify) &&
             (VarEvent.xproperty.state == PropertyNewValue) &&
             (VarEvent.xproperty.window == ParamMozillaWindow) &&
             (VarEvent.xproperty.atom == ParamResponseAtom))
    {
      /* Get response from Mozilla */
      /*                           */
      VarResult = private_WWHelp_Platform_WindowPropertyGet(pParamDisplay,
                                                            ParamMozillaWindow,
                                                            ParamResponseAtom,
                                                            VarResponse,
                                                            WWHELP_MOZILLARESPONSE_MAXLENGTH);
      if (VarResult == 0)
      {
        /* Check response */
        /*                */
        if (strncmp((const char*)VarResponse, "200", 3) == 0)
        {
          /* 200 indicates successful completion */
          /*                                     */
          bVarDone = 1;

          /* Success! */
          /*          */
          VarReturnCode = 0;
        }
        else if (strncmp((const char*)VarResponse, "1", 1) == 0)
        {
          /* Leading 1 indicates positive preliminary reply */
          /*                                                */
        }
        else
        {
          /* Any other response indicates failure */
          /*                                      */
          bVarDone = 1;
        }
      }
      else
      {
        /* Unable to read Mozilla response */
        /*                                 */
        bVarDone = 1;
      }
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_Startup                                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_Startup()
{
  int  ReturnCode = -1;


  /* Nothing to do */
  /*               */
  ReturnCode = 0;

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_Shutdown                                                 */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_Shutdown()
{
  int  ReturnCode = -1;


  /* Nothing to do */
  /*               */
  ReturnCode = 0;

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_SetDefaultBrowserInfo                                    */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_SetDefaultBrowserInfo(WWHelp_BrowserInfo* pParamBrowserInfo)
{
  int         VarReturnCode = -1;
  char*       pVarGetEnvPath;
  char*       pVarMutablePath;
  char*       pVarDirectoryName;
  const char* VarProgramList[WWHELP_PROGRAMARRAY_MAXLENGTH];
  int         VarMaxIndex;
  int         VarIndex;
  char        VarProgramFileName[WWHELP_COMMAND_MAXLENGTH];
  int         VarFailCheckReturnCode;


  /* Reset browser info */
  /*                    */
  WWHelp_Comm_ClearBrowserInfo(pParamBrowserInfo);

  /* No Unix browser supports smart URLs */
  /*                                     */
  pParamBrowserInfo->mbAllowSmartURLs = 0;

  /* Get the user's PATH */
  /*                     */
  pVarGetEnvPath = (char*)getenv("PATH");
  if (pVarGetEnvPath != NULL)
  {
    /* Copy path to a mutable copy for use with strtok */
    /*                                                 */
    pVarMutablePath = (char*)malloc(strlen(pVarGetEnvPath) * sizeof(char) + 1);
    if (pVarMutablePath != NULL)
    {
      strcpy(pVarMutablePath, pVarGetEnvPath);

      /* Create a list of browsers to look for */
      /*                                       */
      VarMaxIndex = 0;
      VarProgramList[VarMaxIndex++] = "netscape6";
      VarProgramList[VarMaxIndex++] = "netscape";
      VarProgramList[VarMaxIndex++] = "mozilla";
      VarProgramList[VarMaxIndex++] = "iexplorer";

      /* Check user's path for browsers */
      /*                                */
      pVarDirectoryName = strtok(pVarMutablePath, ":");
      while (pVarDirectoryName != NULL)
      {
        VarIndex = 0;
        while ((VarReturnCode == -1) &&
               (VarIndex < VarMaxIndex))
        {
          /* Build program name */
          /*                    */
          VarProgramFileName[0] = '\0';
          strcat(VarProgramFileName, pVarDirectoryName);
          strcat(VarProgramFileName, "/");
          strcat(VarProgramFileName, VarProgramList[VarIndex]);

          /* Test program */
          /*              */
          VarReturnCode = private_WWHelp_Platform_FileIsExecutable(VarProgramList[VarIndex]);
          if (VarReturnCode == 0)
          {
            /* Success! */
            /*          */
            strcpy(pParamBrowserInfo->mProgram, VarProgramList[VarIndex]);
          }

          VarIndex++;
        }

        pVarDirectoryName = strtok(NULL, ":");
      }

      /* Free mutable path */
      /*                   */
      free(pVarMutablePath);
    }
  }

  /* Look for browsers in well known locations */
  /*                                           */
  if (VarReturnCode == -1)
  {
    /* Create a list of browsers to look for */
    /*                                       */
    VarMaxIndex = 0;

    /* Netscape 6 */
    /*            */
#if 0
    VarProgramList[VarMaxIndex++] = "/opt/netscape6/netscape6";
    VarProgramList[VarMaxIndex++] = "/usr/local/bin/netscape6";
    VarProgramList[VarMaxIndex++] = "/usr/local/netscape6/netscape6";
    VarProgramList[VarMaxIndex++] = "/usr/bin/netscape6";
    VarProgramList[VarMaxIndex++] = "/usr/dt/bin/netscape6";
#endif

    /* Netscape 4.x */
    /*              */
    VarProgramList[VarMaxIndex++] = "/opt/netscape/netscape";
    VarProgramList[VarMaxIndex++] = "/usr/local/bin/netscape";
    VarProgramList[VarMaxIndex++] = "/usr/local/netscape/netscape";
    VarProgramList[VarMaxIndex++] = "/usr/bin/netscape";
    VarProgramList[VarMaxIndex++] = "/usr/dt/bin/netscape";

    /* Mozilla */
    /*         */
    VarProgramList[VarMaxIndex++] = "/opt/mozilla/mozilla";
    VarProgramList[VarMaxIndex++] = "/usr/local/bin/mozilla";
    VarProgramList[VarMaxIndex++] = "/usr/local/mozilla/mozilla";
    VarProgramList[VarMaxIndex++] = "/usr/bin/mozilla";
    VarProgramList[VarMaxIndex++] = "/usr/dt/bin/mozilla";

    /* Internet Explorer */
    /*                   */
    VarProgramList[VarMaxIndex++] = "/usr/local/microsoft/bin/iexplorer";

    /* Check browsers */
    /*                */
    VarIndex = 0;
    while ((VarReturnCode == -1) &&
           (VarIndex < VarMaxIndex))
    {
      VarReturnCode = private_WWHelp_Platform_FileIsExecutable(VarProgramList[VarIndex]);
      if (VarReturnCode == 0)
      {
        /* Success! */
        /*          */
        strcpy(pParamBrowserInfo->mProgram, VarProgramList[VarIndex]);
      }

      VarIndex++;
    }
  }

  /* Determine if found browser is a Mozilla browser */
  /*                                                 */
  if (VarReturnCode == 0)
  {
    if ((strcmp(pParamBrowserInfo->mProgram, "netscape6") == 0) ||
        (strstr(pParamBrowserInfo->mProgram, "/netscape6") != NULL))
    {
      pParamBrowserInfo->mbNetscapeBrowser = 1;
    }
    else if ((strcmp(pParamBrowserInfo->mProgram, "netscape") == 0) ||
             (strstr(pParamBrowserInfo->mProgram, "/netscape") != NULL))
    {
      pParamBrowserInfo->mbNetscapeBrowser = 1;
    }
    else if ((strcmp(pParamBrowserInfo->mProgram, "mozilla") == 0) ||
             (strstr(pParamBrowserInfo->mProgram, "/mozilla") != NULL))
    {
      pParamBrowserInfo->mbNetscapeBrowser = 1;
    }

    /* Set the process signature for use with Mozilla browsers */
    /*                                                         */
    if (pParamBrowserInfo->mbNetscapeBrowser)
    {
      /* Set process signature */
      /*                       */
      VarReturnCode = private_WWHelp_Platform_SetProcessSignature(pParamBrowserInfo->mProcessSignature);
    }
  }

  /* Determine if this browser setup suppresses all errors */
  /*                                                       */
  if (VarReturnCode == 0)
  {
    VarFailCheckReturnCode = private_WWHelp_Platform_Command(1, pParamBrowserInfo->mProgram,
                                                             "-remote", NULL);
    if (VarFailCheckReturnCode == 0)
    {
      /* Browser will never report a failure */
      /*                                     */
      pParamBrowserInfo->mbNeverFails = 1;
    }
    else
    {
      pParamBrowserInfo->mbNeverFails = 0;
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_CheckBrowserRunning                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_CheckBrowserRunning(const WWHelp_BrowserInfo* pParamBrowserInfo)
{
  int      VarReturnCode = -1;
  Display* pVarDisplay;
  Atom     VarVersionAtom;
  Window   VarMozillaWindow;


  /* Only Mozilla browsers can be detected */
  /*                                       */
  if (pParamBrowserInfo->mbNetscapeBrowser)
  {
    /* NOTE: Running Mozilla browser may not match the one found */
    /*       on the command line.                                */
    /*                                                           */

    /* Access current display */
    /*                        */
    pVarDisplay = XOpenDisplay("");

    /* Get Mozilla version atom */
    /*                          */
    VarVersionAtom = XInternAtom(pVarDisplay, WWHELP_MOZILLA_VERSION_PROP, False);

    /* Find Mozilla window */
    /*                     */
    VarMozillaWindow = private_WWHelp_Platform_FindMozillaWindow(pVarDisplay, VarVersionAtom);
    if (VarMozillaWindow)
    {
      /* Success! */
      /*          */
      VarReturnCode = 0;
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_SendURL                                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_SendURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                        const char*               pParamURL,
                        const size_t              ParamURLLength)
{
  int      VarReturnCode = -1;
  char     VarOpenURLCommand[WWHELP_URL_MAXLENGTH];
  size_t   VarOpenURLLength;
  int      VarResult;
  Display* pVarDisplay;
  Atom     VarVersionAtom;
  Atom     VarLockAtom;
  Atom     VarCommandAtom;
  Atom     VarResponseAtom;
  Window   VarMozillaWindow;


  /* Send URL to running browser */
  /*                             */
  if (pParamBrowserInfo->mbNetscapeBrowser)
  {
    /* NOTE: Running Mozilla browser may not match the one found */
    /*       on the command line.                                */
    /*                                                           */

    /* Create openURL message */
    /*                        */
    strcpy(VarOpenURLCommand, "openURL(");
    VarOpenURLLength = strlen(VarOpenURLCommand);
    memcpy(&(VarOpenURLCommand[VarOpenURLLength]), pParamURL, ParamURLLength);
    VarOpenURLCommand[VarOpenURLLength + ParamURLLength] = '\0';
    strcat(VarOpenURLCommand, ")");

    /* Access current display */
    /*                        */
    pVarDisplay = XOpenDisplay("");
    if (pVarDisplay)
    {
      /* Get Mozilla atoms */
      /*                   */
      VarVersionAtom = XInternAtom(pVarDisplay, WWHELP_MOZILLA_VERSION_PROP, False);
      VarLockAtom = XInternAtom(pVarDisplay, WWHELP_MOZILLA_LOCK_PROP, False);
      VarCommandAtom = XInternAtom(pVarDisplay, WWHELP_MOZILLA_COMMAND_PROP, False);
      VarResponseAtom = XInternAtom(pVarDisplay, WWHELP_MOZILLA_RESPONSE_PROP, False);

      /* Find Mozilla window */
      /*                     */
      VarMozillaWindow = private_WWHelp_Platform_FindMozillaWindow(pVarDisplay, VarVersionAtom);
      if (VarMozillaWindow)
      {
        /* Lock Mozilla window */
        /*                     */
        VarResult = private_WWHelp_Platform_LockMozillaWindow(pVarDisplay,
                                                              VarMozillaWindow,
                                                              VarLockAtom,
                                                              pParamBrowserInfo->mProcessSignature);
        if (VarResult == 0)
        {
          /* Send Mozilla command */
          /*                      */
          VarResult = private_WWHelp_Platform_SendMozillaRemoteCommand(pVarDisplay,
                                                                       VarMozillaWindow,
                                                                       VarCommandAtom,
                                                                       VarResponseAtom,
                                                                       pParamBrowserInfo->mProcessSignature,
                                                                       (unsigned char*)VarOpenURLCommand);
          if (VarResult == 0)
          {
            /* Success! */
            /*          */
            VarReturnCode = 0;
          }

          /* Unlock Mozilla window */
          /*                       */
          VarResult = private_WWHelp_Platform_UnlockMozillaWindow(pVarDisplay,
                                                                  VarMozillaWindow,
                                                                  VarLockAtom,
                                                                  pParamBrowserInfo->mProcessSignature);
          if (VarResult == 0)
          {
            /* Unlock succeeded */
            /*                  */
          }
        }
      }
    }
  }

  return VarReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_LaunchWithURL                                            */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_LaunchWithURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                              const char*               pParamURL,
                              const size_t              ParamURLLength)
{
  int   VarReturnCode = -1;
  char  VarURLString[WWHELP_URL_MAXLENGTH];


  /* Create a NULL terminated string from the given URL */
  /*                                                    */
  memcpy(VarURLString, pParamURL, ParamURLLength);
  VarURLString[ParamURLLength] = '\0';

  /* Launch new browser with URL */
  /*                             */
  VarReturnCode = private_WWHelp_Platform_Command(0, pParamBrowserInfo->mProgram,
                                                  VarURLString, 0);

  return VarReturnCode;
}
