/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = PARALLEL.C
 *
 * DESCRIPTIVE NAME = Parallel port driver
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION
 *
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define LINT_ARGS                      /* argument checking enabled         */

#define  INCL_DOS
#define  INCL_GPI
#undef   INCL_GPI
#define  INCL_DEV
#define  INCL_DOSMEMMGR                /* Include standard OS/2 support     */
#define  INCL_DOSMODULEMGR             /* For DosLoadModule                 */
#define  INCL_DOSPROCESS
#define  INCL_GPILCIDS
#define  INCL_WINCOMMON                /* Include Window Management support */
#define  INCL_WINDOWMGR
#define  INCL_WINSWITCHLIST
#define  INCL_WINPROGRAMLIST
#define  INCL_WINMENUS
#define  INCL_WINWINDOWMGR
#define  INCL_WINMESSAGEMGR
#define  INCL_WINDIALOGS
#define  INCL_WINSTATICS
#define  INCL_WINLISTBOXES
#define  INCL_WINMENUS
#define  INCL_WINSYS
#define  INCL_WINFRAMEMGR
#define  INCL_INCLWINACCELERATORS
#define  INCL_WINPOINTERS
#define  INCL_WINERRORS
#define  INCL_WINSHELLDATA

#define  INCL_WINTYPES
#define  INCL_WINACCELERATORS
#define  INCL_WINBUTTONS
#define  INCL_WINENTRYFIELDS
#define  INCL_WINRECTANGLES
#define  INCL_WINTIMER
#define  INCL_WINSCROLLBARS
#define  INCL_WINHEAP
#define  INCL_SHLERRORS
#define  INCL_WININPUT
#define  INCL_WINHELP
#define  INCL_WINSTDSPIN

#define  INCL_SPL
#define  INCL_SPLP
#define  INCL_SPLERRORS
#define  INCL_SHLERRORS
#define  INCL_DOSERRORS
#define  INCL_WINHOOKS

int acrtused=1;                     /* Define variable to say this is a DLL */

#include    <os2.h>

#include    <stdlib.h>
#include    <string.h>

#include    "PARALLEL.h"
#include    "port.h"

/*
** If port driver is not defined in INI file yet
**   assume it exists in the boot drive's \OS2\DLL directory
*/
CHAR szDefaultPortDrvPath[] = "?:\\OS2\\DLL\\PARALLEL.PDR";


/*
** Below definition of PORTNAMES structure should be defined in
** common header file pmspl.h.
*/
typedef struct _PORTNAMES
{
   PSZ pszPortName;         /* -> name of port(ie "LPT1)                    */
   PSZ pszPortDesc;         /* -> description of port(ie "Parallel Port 1") */
} PORTNAMES, *PPORTNAMES;


/*
** We want to avoid automatically loading the help manager(HELPMGR.DLL),
**   since this takes up lots of memory.
** Do this by only linking to the HELPMGR if user selects help item.
** We replace the WinxxxxHelpInstance calls with our local versions.
** These versions use DosLoadModule() and DosQueryProcAddr() to
**   call the "real" help manager functions.
** The below function pointers, prototypes and variables are used
**   to this end.
*/

BOOL APIENTRY CALLAssociateHelpInstance (HWND hwndHelpInstance, HWND hwndApp);
BOOL APIENTRY CALLDestroyHelpInstance(HWND hwndHelpInstance);
HWND APIENTRY CALLCreateHelpInstance(HAB hab,PHELPINIT phinitHMInitStructure);
HWND APIENTRY CALLQueryHelpInstance(HWND hwndApp);

BOOL (* APIENTRY pfnWinAssociateHelpInstance)(HWND, HWND);
BOOL (* APIENTRY pfnWinCreateHelpInstance)(HAB, PHELPINIT);
BOOL (* APIENTRY pfnWinDestroyHelpInstance)(HWND);
BOOL (* APIENTRY pfnWinQueryHelpInstance)(HWND);

VOID EXPENTRY InitializeHelp(VOID);
BOOL EXPENTRY SetHelpStubHook(VOID);
VOID EXPENTRY ReleaseHelpStubHook(VOID);
INT  EXPENTRY HelpStubHook( HAB AppHAB, USHORT Context, USHORT IdTopic,
                            USHORT IdSubTopic, PRECTL RectLPtr );

/*
** Global handle for helpmgr module, so it is only loaded once
*/
HMODULE  hvHelpmgrModule;

HWND hwndHelp ;
BOOL HelpStubHookIsSet;                                         /* for help */
BOOL HelpAlreadyInitialized;                                    /* for help */


/*
** HELPINIT -- help manager initialization structure
*/
static HELPINIT hmiHelpData = {
    sizeof(HELPINIT),
    0L,
    (PSZ)     NULL,
    (PHELPTABLE) NULL,
    (HMODULE) NULL,
    (HMODULE) NULL,
    (USHORT)  NULL,
    (USHORT)  NULL,
    (PSZ)     NULL,
    CMIC_HIDE_PANEL_ID,
    (PSZ)     PRT_HELPFILE_NAME
};

/*
** Local Functions
*/
ULONG  CalcBufLength ( HAB hab, HMODULE hModule );
ULONG  CalcStructLength ( HAB hab, HMODULE hModule, USHORT usID );
ULONG  NumPortsCanFit ( HAB hab, HMODULE hModule, ULONG cbBuf );
VOID   CopyNPorts ( HAB hab, HMODULE hModule, PCH pBuf, ULONG ulReturned ) ;
VOID   CopyStruct ( HAB hab, HMODULE hModule, USHORT usID, PCH pBuf,
                   PULONG pulBeginStruct, PULONG pulBeginText );
BOOL   GetPortDescription ( HAB hab, HMODULE hModule, PSZ pszPortName,
                            PSZ pszPortDesc );
ULONG  OpenParallPortDlg ( HAB hab, HMODULE hModule, PSZ pszPortName,
                           PSZ pszAppName );
BOOL   GetPortSettings( PPARALLDATA pParallData );
BOOL   SavePortSettings(PPARALLDATA pParallData, HWND hDlg);
VOID   RemoveLeadTrailBlanks ( PCH pTarget, PCH pSource );
USHORT DisplayError ( HWND hwndOwner, HMODULE hModule,
                      USHORT usStringID, USHORT usWinStyle );
VOID   DE ( PCH str );
MRESULT EXPENTRY ParallDlg( HWND hDlg, USHORT msg, MPARAM mp1, MPARAM mp2 );


/****************************************************************************
 *
 * FUNCTION NAME = SplPdEnumPort
 *
 * DESCRIPTION   = Return ports supported by this port driver
 *                 Currently this will return those ports this port
 *                  driver supports by default.
 *                 Future enhancement might be to also return any
 *                  ports that have been added that now use this
 *                  port driver.
 *
 * INPUT         = hab - anchor block handle
 *                 pBuf - buffer to get enumerated PORTNAMES structures
 *                 cbBuf - size(in bytes) of pBuf passed in
 *
 * OUTPUT        = *pulReturned - number of PORTNAMES structures stored in pBuf
 *                 *pulTotal    - total ports supported by this port driver
 *                 *pcbRequired - size(in bytes) of buffer needed to store
 *                                all enumerated PORTNAMES entries.
 *                 pBuf - gets an array(number elements is *pulReturned) of
 *                        PORTNAMES structures.
 *                        Each psz in PORTNAMES structure points to a string
 *                        copied into the end of pBuf.
 *
 *                 typedef struct _PORTNAMES {
 *                         PSZ pszPortName;  // Name of port, example: LPT1
 *                         PSZ pszPortDesc;  // Port description
 *                 } PORTNAMES;
 *
 * RETURN-NORMAL = 0 - if all portnames and descriptions fit in pBuf
 *
 * RETURN-ERROR  = ERROR_INSUFFICIENT_BUFFER - if no PORTNAMES structs
 *                                             could fit in buffer.  Caller
 *                                             uses *pcbRequired to allocate
 *                                             a buffer big enough to store all
 *                                             port names.
 *                 ERROR_MORE_DATA - if some, but not all, PORTNAMES structs
 *                                   could fit in buffer.  Caller
 *                                   uses *pcbRequired to allocate
 *                                   a buffer big enough to store all
 *                                   port names.
 *
 *      NOTE: early versions of the print object expected ERROR_MORE_DATA
 *            to be returned, even when no PORTNAMES structures would fit.
 *            For this reason, we do not return ERROR_INSUFFICIENT_BUFFER
 *
 ****************************************************************************/

APIRET APIENTRY SplPdEnumPort ( HAB hab,
                                PVOID pBuf,
                                ULONG cbBuf,
                                PULONG pulReturned,
                                PULONG pulTotal,
                                PULONG pcbRequired )

{
   HMODULE hModule;
   ULONG   ulBootDrive;
   ULONG   rcLoadMod;
   CHAR    szPathName[260]; /* will contain full path to this port driver */

      /*
      ** ensure pointers not null
      */
   if (!pulReturned ||
       !pulTotal ||
       !pcbRequired)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** if buffer length is supplied then there should be pBuf
      */
   if (!pBuf && cbBuf)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** We need our module handle.
      ** Easiest way to do this is to find the path to our port driver
      **  from the system INI file.
      ** If not found in the INI file
      **  assume it is in the boot drive's \OS2\DLL directory
      */

   /* change ? in szDefaultPortDrvPath to boot drive */
   DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive,
                    sizeof (ULONG));
   szDefaultPortDrvPath[0] = (CHAR)(ulBootDrive + 'A' - 1);

   PrfQueryProfileString (HINI_SYSTEMPROFILE,
                          "PM_PORT_DRIVER",
                          "PARALLEL",
                          szDefaultPortDrvPath,
                          szPathName,
                          256 );

      /*
      ** get module handle for our dll
      */
   rcLoadMod = DosLoadModule (NULL, 0, szPathName, &hModule);

      /*
      ** check if cbBuf is 0 - then return number of ports in pulTotal
      ** and number of bytes required in pcbRequired
      */
   if (cbBuf == 0L)
   {
      *pulReturned = 0;
      *pcbRequired = CalcBufLength (hab, hModule);
      *pulTotal    = 3 ;            /* Currently support LPT1 LPT2 and LPT3 */
      if (!rcLoadMod)
        DosFreeModule (hModule);

           /*
           ** NOTE: early version of the print object checked for
           **       ERROR_MORE_DATA instead of ERROR_INSUFFICIENT_BUFFER
           **       For this reason we return ERROR_MORE_DATA here.
           */
        return(ERROR_MORE_DATA);
   }

      /*
      ** check number of ports info we can fit in supplied buffer
      */
   *pulTotal    = 3 ; /* Currently support LPT1 LPT2 and LPT3 */
   *pcbRequired = CalcBufLength (hab, hModule);
   *pulReturned = NumPortsCanFit (hab, hModule, cbBuf);

      /*
      ** return error if we can not fit one port.
      */
   if (!(*pulReturned))
   {
      if (!rcLoadMod)
        DosFreeModule (hModule);
      return(ERROR_INSUFFICIENT_BUFFER);
   }

      /*
      ** copy all the ports which we can store in the pBuf
      */
   CopyNPorts (hab, hModule, (PCH)pBuf, *pulReturned);

      /*
      ** Free the module - we do not need it any more.
      */
   if (!rcLoadMod)
     DosFreeModule (hModule);

      /*
      ** copy all the ports which we can store in the pBuf
      */
   if (*pulReturned < *pulTotal)
   {
      return(ERROR_MORE_DATA);
   }

   return(NO_ERROR);
}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdInitPort
 *
 * DESCRIPTION   = Initialize port on behalf of the spooler
 *
 * INPUT         = hFile       - File handle to port
 *                 pszPortName - name of port to initialize
 *
 * OUTPUT        = Sets Parallel port settings(port timeout)
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = ERROR_INVALID_PARAMETER - if     port name given
 *
 ****************************************************************************/

APIRET APIENTRY SplPdInitPort ( HFILE hFile,
                                PSZ pszPortName )
{
    USHORT usParamPacket= 0;
    USHORT usDataPacket = 0;
    UCHAR  bInfRetry;
    CHAR   chBuf[STR_LEN_PORTNAME];
    CHAR   chInitVal[STR_LEN_PORTNAME];
    PCH    pchPortDriver = chInitVal;
    ULONG  ulReturned1;
    ULONG  ulReturned2;

      /*
      ** Check if port name string is NULL. This is an error.
      */
   if (!pszPortName)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** Make Application name string( "PM_PortName" )
      */
   strcpy (chBuf, APPNAME_LEAD_STR);
   strcat (chBuf, pszPortName);

      /*
      ** Check if this port is parallel port.
      */
   if (!(PrfQueryProfileString (HINI_SYSTEMPROFILE, chBuf,
                                KEY_PORTDRIVER, NULL, pchPortDriver,
                                STR_LEN_PORTNAME)))
   {
      return(ERROR_INVALID_PARAMETER);
   }

   if (strcmp (pchPortDriver, DEF_PORTDRIVER))
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** Check if this port is installed.
      */
   if (!(PrfQueryProfileString (HINI_SYSTEMPROFILE, chBuf,
                                KEY_TIMEOUT, NULL, chInitVal,
                                STR_LEN_PORTNAME)))
   {
      return(ERROR_INVALID_PARAMETER);
   }
      /*
      ** Set printer timeout
      */
    usDataPacket = (USHORT)atoi (chInitVal);

    ulReturned2 = 2L ;

    DosDevIOCtl(
                hFile,                    /* Hfile passed into SplPdInit */
                0x5,                      /* Category number             */
                0x4e,                     /* Function number             */
                NULL,                     /* pParams - not needed        */
                0,                        /* sizeof *pParms              */
                NULL,                     /* &size in/out                */
                (PVOID) &usDataPacket,    /* points to LPT timeout value */
                sizeof (USHORT),          /* sizeof usDataPacket         */
                &ulReturned2);            /* &size in/out                */


      /*
      ** Check for initialization settings for multiple hardware access
      **
      ** If not there, or not equal to "1;"
      **   disallow multiple hardware access
      ** else(if equal to "1;")
      **   allow multiple hardware access to this LPT port
      **
      */
   if ( (PrfQueryProfileString (HINI_SYSTEMPROFILE, chBuf,
                                KEY_INITIALIZATION, NULL, chInitVal,
                                STR_LEN_PORTNAME))
        && ( chInitVal[0] == '1' ) )
   {
      /* Set to allow multiple hardware access by setting bit 0 on   */
      usDataPacket = 0x0001 ;
   }
   else
   {
      /* Set to not allow multiple hardware access by clearing bit 0 */
      usDataPacket = 0x0000 ;
   }
      /*
      ** Set multiple hardware access
      */

    /* Set return sizes so IOCTL will work */
    ulReturned1 = 1L ;
    ulReturned2 = 1L ;

    DosDevIOCtl(
                hFile,                    /* Hfile passed into SplPdInit */
                0x5,                      /* Category number             */
                0x51,                     /* Function number             */
                (PVOID) &usParamPacket,   /* -> command byte - must be 0 */
                sizeof (BYTE),
                &ulReturned1,
                (PVOID) &usDataPacket,    /* points to flag byte         */
                sizeof (BYTE),
                &ulReturned2);


      /*
      ** Set infinite retry to false.
      */
    bInfRetry = (UCHAR)FALSE;
    DosDevIOCtl(
                 hFile,
                 0x05,
                 0x44,
                 NULL,
                 0L,
                 NULL,
                 (PCH)&bInfRetry,
                 sizeof (USHORT),
                 &ulReturned2);

   return(NO_ERROR);
}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdInstallPort
 *
 * DESCRIPTION   = Tells port driver the name of a port that needs to be
 *                   installed.
 *                 Port driver should write Initialization/Termination
 *                   strings to the INI file.
 *                 Typically SplPdSetPort will be called after this.
 *                 This should allow any port to be added for this port driver.
 *                   (ie: if it is not a port we returned in SplPdEnumPort,
 *                          still allow the port to use this port driver).
 *
 * INPUT         = hab         - Anchor block handle
 *                 pszPortName - name of port to be installed
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = ERROR_INVALID_PARAMETER - if     port name given
 *
 ****************************************************************************/

APIRET APIENTRY SplPdInstallPort ( HAB hab,
                                   PSZ pszPortName )
{
   CHAR    chBuf[STR_LEN_PORTNAME];
   CHAR    chPortDesc[STR_LEN_PORTDESC];
   HMODULE hModule;
   ULONG   ulBootDrive;
   CHAR    szPathName[260];   /* will contain full path to this port driver */

      /*
      ** Check if port name string is NULL. This is an error.
      */
   if (!pszPortName)
   {
      return(ERROR_INVALID_PARAMETER);
   }
      /*
      ** We need our module handle.
      ** Easiest way to do this is to find the path to our port driver
      **  from the system INI file.
      ** If not found in the INI file
      **  assume it is in the boot drive's \OS2\DLL directory
      */

   /* change ? in szDefaultPortDrvPath to boot drive */
   DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive,
                    sizeof (ULONG));
   szDefaultPortDrvPath[0] = (CHAR)(ulBootDrive + 'A' - 1);

   PrfQueryProfileString (HINI_SYSTEMPROFILE,
                          "PM_PORT_DRIVER",
                          "PARALLEL",
                          szDefaultPortDrvPath,
                          szPathName,
                          256 );

   hModule = 0L ;                             /* Init module handle to null */

      /*
      ** get module handle for our dll
      */
   DosLoadModule (NULL, 0, szPathName, &hModule);

      /*
      ** Check if we have description for port
      */
   if (!GetPortDescription (hab, hModule, pszPortName, chPortDesc))
   {
      /*
      ** Port description not found, use port name
      */
      strcpy( chPortDesc, pszPortName );
   }

      /*
      ** Free the module
      */
   DosFreeModule (hModule);

      /*
      ** Make Application name string.
      */
   strcpy (chBuf, APPNAME_LEAD_STR);
   strcat (chBuf, pszPortName);

      /*
      ** Write data for this port in ini file with new format.
      */
   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          chBuf,
                          KEY_DESCRIPTION,
                          chPortDesc);

   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          chBuf,
                          KEY_INITIALIZATION,
                          DEF_INITIALIZATION);

   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          chBuf,
                          KEY_TERMINATION,
                          DEF_TERMINATION);

   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          chBuf,
                          KEY_TIMEOUT,
                          DEF_TIMEOUT);

   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          chBuf,
                          KEY_PORTDRIVER,
                          DEF_PORTDRIVER);

      /*
      ** Write data for this port in ini file with old format.
      */
   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          APPNAME_PM_SPOOLER_PORT,
                          pszPortName,
                          DEF_OLD_INITIALIZATION);

   return(NO_ERROR);
}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdGetPortIcon
 *
 * DESCRIPTION   = Return Resource ID of icon representing this port.
 *                 Note: only one icon will represent all ports supported
 *                       by a port driver.
 *
 * INPUT         = hab         - Anchor block handle
 *                 idIcon      - gets Resource ID of icon bit map
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = TRUE
 *
 * RETURN-ERROR  = FALSE - if unable to return icon Resource ID
 *
 ****************************************************************************/

BOOL   APIENTRY SplPdGetPortIcon ( HAB hab,
                                   PULONG idIcon )
{
      /*
      ** Check for our global port icon ID(is always set)
      */
   if (idIcon)
   {
      *idIcon = PARALLEL_ICON;
   }
   return(TRUE);
}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdQueryPort
 *
 * DESCRIPTION   = Returns textual data that describes the port configuration.
 *
 * INPUT         = hab         - Anchor block handle
 *                 pszPortName - name of port to get configuration for
 *                 pBufIn      - pointer to buffer of returned data structures
 *                 cbBuf       - Size of pBufIn in bytes
 *                 cItems      - Count of number of strings of descriptions
 *                               returned
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = ERROR_INSUFFICIENT_BUFFER - if buffer is too small
 *                 ERROR_INVALID_PARAMETER - if     port name given
 *
 ****************************************************************************/

APIRET APIENTRY SplPdQueryPort ( HAB hab,
                                 PSZ pszPortName,
                                 PVOID pBufIn,
                                 ULONG cbBuf,
                                 PULONG cItems )
{
   HMODULE hModule;
   CHAR chString[STR_LEN_DESC];
   USHORT usNumLines;
   USHORT usLineID;
   USHORT usStrLength;
   ULONG  ulBootDrive;
   PCH    pBuf = (PCH)pBufIn;
   CHAR   szPathName[260];    /* will contain full path to this port driver */

      /*
      ** check pointer to all the return variables is not null
      */
   if (!cItems)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** if pBuf or cbBuf is NULL - it is an error.
      */
   if (!pBuf || !cbBuf)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** We need our module handle.
      ** Easiest way to do this is to find the path to our port driver
      **  from the system INI file.
      ** If not found in the INI file
      **  assume it is in the boot drive's \OS2\DLL directory
      */

   /* change ? in szDefaultPortDrvPath to boot drive */
   DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive,
                    sizeof (ULONG));
   szDefaultPortDrvPath[0] = (CHAR)(ulBootDrive + 'A' - 1);

   PrfQueryProfileString (HINI_SYSTEMPROFILE,
                          "PM_PORT_DRIVER",
                          "PARALLEL",
                          szDefaultPortDrvPath,
                          szPathName,
                          256 );

   hModule = 0L ;                             /* Init module handle to null */

      /*
      ** get module handle for our dll
      */
   DosLoadModule (NULL, 0, szPathName, &hModule);

   chString[0] = '\0' ;

      /*
      ** get number of lines.
      */
   WinLoadString(hab, hModule, (USHORT)ID_NUMBER_OF_DESC_LINES, STR_LEN_DESC,
                 chString);
   usNumLines = (USHORT)atoi (chString);
   usLineID = ID_FIRST_DESC_LINES;
   for (*cItems = 0; *cItems < usNumLines; *cItems++)
   {
      WinLoadString(hab, hModule, usLineID, STR_LEN_DESC, chString);
      if (cbBuf >= (usStrLength = (USHORT)(strlen (chString) + 1)))
      {
         strcpy (pBuf, chString);
         pBuf += usStrLength;
         cbBuf -= usStrLength;
      }
      else
      {
         DosFreeModule (hModule);
         return(ERROR_INSUFFICIENT_BUFFER);
      }
   }
   DosFreeModule (hModule);
   return(NO_ERROR);

}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdSetPort
 *
 * DESCRIPTION   = Display a dialog to allow the user to browse and modify
 *                 port configurations.
 *
 * INPUT         = hab         - Anchor block handle
 *                 pszPortName - name of port to configure
 *                 flModified  - Flag to indicate that the configuration
 *                               has been modified.(TRUE if modified).
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = ERROR_INVALID_PARAMETER - if     port name given
 *
 ****************************************************************************/

APIRET APIENTRY SplPdSetPort ( HAB hab,
                               PSZ pszPortName,
                               PULONG flModified )
{
    CHAR chBuf[STR_LEN_PORTNAME];
    CHAR chPortDriver[STR_LEN_PORTNAME];
    HMODULE hModule;
    ULONG   ulBootDrive;
    CHAR    szPathName[260];   /* will contain full path to this port driver */

      /*
      ** Check if port name string is NULL. This is an error.
      */
   if (!pszPortName || !flModified)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** Make Application name string( "PM_PortName" ).
      */
   strcpy (chBuf, APPNAME_LEAD_STR);
   strcat (chBuf, pszPortName);

      /*
      ** Check if this port is parallel port.
      */
   if (!(PrfQueryProfileString (HINI_SYSTEMPROFILE, chBuf,
                                KEY_PORTDRIVER, NULL, chPortDriver,
                                STR_LEN_PORTNAME)))
   {
      return(ERROR_INVALID_PARAMETER);
   }

   if (strcmp (chPortDriver, DEF_PORTDRIVER))
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** We need our module handle.
      ** Easiest way to do this is to find the path to our port driver
      **  from the system INI file.
      ** If not found in the INI file
      **  assume it is in the boot drive's \OS2\DLL directory
      */

   /* change ? in szDefaultPortDrvPath to boot drive */
   DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive,
                    sizeof (ULONG));
   szDefaultPortDrvPath[0] = (CHAR)(ulBootDrive + 'A' - 1);

   PrfQueryProfileString (HINI_SYSTEMPROFILE,
                          "PM_PORT_DRIVER",
                          "PARALLEL",
                          szDefaultPortDrvPath,
                          szPathName,
                          256 );

   hModule = 0L ;                             /* Init module handle to null */

      /*
      ** get module handle for our dll
      */
   DosLoadModule (NULL, 0, szPathName, &hModule);

      /*
      ** Load the dialog for user to change.
      */
   *flModified = OpenParallPortDlg (hab, hModule, pszPortName, chBuf);

      /*
      ** free the module
      */
   DosFreeModule (hModule);
   return(NO_ERROR);
}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdRemovePort
 *
 * DESCRIPTION   = Tells port driver the name of a port that needs to be removed.
 *                 Port driver should remove its data from the INI file.
 *
 * INPUT         = hab         - Anchor block handle
 *                 pszPortName - name of port to be removed
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = ERROR_INVALID_PARAMETER - if     port name given
 *
 ****************************************************************************/

APIRET APIENTRY SplPdRemovePort ( HAB hab,
                                  PSZ pszPortName )
{
    CHAR chBuf[STR_LEN_PORTNAME];
    CHAR chPortDriver[STR_LEN_PORTNAME];

      /*
      ** Check if port name string is NULL. This is an error.
      */
   if (!pszPortName)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** Make Application name string.
      */
   strcpy (chBuf, APPNAME_LEAD_STR);
   strcat (chBuf, pszPortName);

      /*
      ** Check if this port is parallel port.
      */
   if (!(PrfQueryProfileString (HINI_SYSTEMPROFILE, chBuf,
                                KEY_PORTDRIVER, NULL, chPortDriver,
                                STR_LEN_PORTNAME)))
   {
      return(ERROR_INVALID_PARAMETER);
   }

   if (strcmp (chPortDriver, DEF_PORTDRIVER))
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** We found port to be removed.
      ** Remove it from new format "PM_portname"
      */
   PrfWriteProfileString (HINI_SYSTEMPROFILE, chBuf, NULL, NULL);

      /*
      ** remove this port from old format.
      */
   PrfWriteProfileString (HINI_SYSTEMPROFILE,
                          APPNAME_PM_SPOOLER_PORT,
                          pszPortName,
                          NULL);
   return(NO_ERROR);

}

/****************************************************************************
 *
 * FUNCTION NAME = SplPdTermPort
 *
 * DESCRIPTION   = Terminate port on behalf of the spooler
 *
 * INPUT         = hFile       - File handle to port
 *                 pszPortName - name of port whose job is completing
 *
 * OUTPUT        = Sets Parallel port settings(port timeout)
 *
 * RETURN-NORMAL = 0
 *
 * RETURN-ERROR  = ERROR_INVALID_PARAMETER - if     port name given
 *
 ****************************************************************************/

SPLERR EXPENTRY SplPdTermPort ( HFILE hFile,
                                PSZ pszPortName )
{
    CHAR chBuf[STR_LEN_PORTNAME];
    PCH chPortDriver[STR_LEN_PORTNAME];

      /*
      ** We do not have to take any action. Return NO_ERROR
      */
   return(NO_ERROR);

#ifdef TERM_ACTION_NEEDED
      /*
      ** Check if port name string is NULL. This is an error.
      */
   if (!pszPortName)
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** Make Application name string.
      */
   strcpy (chBuf, APPNAME_LEAD_STR);
   strcat (chBuf, pszPortName);

      /*
      ** Check if this port is parallel port.
      */
   if (!(PrfQueryProfileString (HINI_SYSTEMPROFILE, chBuf,
                                  KEY_PORTDRIVER, NULL, chPortDriver, 64)))
   {
      return(ERROR_INVALID_PARAMETER);
   }

   if (strcmp ((PSZ)chPortDriver, DEF_PORTDRIVER))
   {
      return(ERROR_INVALID_PARAMETER);
   }

      /*
      ** We do not have to take any action - return NO_ERROR
      */
   return(NO_ERROR);

#endif

}

/****************************************************************************
 *
 * FUNCTION NAME = CalcBufLength
 *
 * DESCRIPTION   = Determine how big buffer is needed to store all PORTNAMES
 *                 structures
 *
 * INPUT         = hab - anchor block handle
 *                 hModule - this port driver's module handle
 *
 * OUTPUT        = length of buffer necessary to store all default port names
 *                 supported by this port driver.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG CalcBufLength ( HAB hab,
                      HMODULE hModule )
{
   ULONG cbRequired;
   USHORT usID;

   cbRequired = 0;

      /*
      ** calculate length required to fit all the port info.
      */
   for (usID = PORT_ID_FIRST; usID <= PORT_ID_LAST; usID += 2)
   {
      cbRequired += CalcStructLength (hab, hModule, usID);
   }

   return(cbRequired);
}

/****************************************************************************
 *
 * FUNCTION NAME = CalcStructLength
 *
 * DESCRIPTION   = Determine size of buffer needed to store PORTNAMES structure
 *                 for given string ID.
 *
 * INPUT         = hab     - anchor block handle
 *                 hModule - this port driver's module handle
 *                 usID    - string ID for port name
 *
 * OUTPUT        = length of buffer necessary to store this port name
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG CalcStructLength ( HAB hab,
                         HMODULE hModule,
                         USHORT usID )
{
   ULONG cbRequired;
   CHAR chString[STR_LEN_PORTDESC];

   cbRequired = 0;

   WinLoadString(hab, hModule, usID, STR_LEN_PORTDESC, chString);
   cbRequired += strlen (chString) + 1;
   WinLoadString(hab, hModule, (USHORT)(usID + 1), STR_LEN_PORTDESC, chString);
   cbRequired += strlen (chString) + 1;
   cbRequired += sizeof (PORTNAMES);
   return(cbRequired);
}

/****************************************************************************
 *
 * FUNCTION NAME = NumPortsCanFit
 *
 * DESCRIPTION   = Determine how many ports can fit in buffer
 *
 * INPUT         = hab     - anchor block handle
 *                 hModule - this port driver's module handle
 *                 cbBuf   - size in bytes of buffer to hold PORTNAMES
 *
 * OUTPUT        = count of PORTNAMES structures that can fit in buffer
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG NumPortsCanFit ( HAB hab,
                       HMODULE hModule,
                       ULONG cbBuf )
{
   ULONG cbRequired;
   USHORT usID;
   ULONG ulNumPort;

   cbRequired = 0;
   ulNumPort = 0;

      /*
      ** calculate how many ports we can fit in buf.
      */
   for (usID = PORT_ID_FIRST; usID <= PORT_ID_LAST; usID += 2)
   {
      cbRequired += CalcStructLength (hab, hModule, usID);
      if (cbRequired > cbBuf)
      {
         return(ulNumPort);
      }
      ulNumPort++;
   }

   return(ulNumPort);
}

/****************************************************************************
 *
 * FUNCTION NAME = CopyNPorts
 *
 * DESCRIPTION   = Copy given number of ports into buffer
 *
 * INPUT         = hab     - anchor block handle
 *                 hModule - this port driver's module handle
 *                 pBuf    - buffer to get PORTNAMES structures
 *                 ulReturned - number of ports to return
 *
 * OUTPUT        = pBuf is updated
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID  CopyNPorts ( HAB hab,
                   HMODULE hModule,
                   PCH pBuf,
                   ULONG ulReturned )
{
   USHORT usID;
   ULONG ulBeginText;
   ULONG ulBeginStruct;

   ulBeginText = ulReturned * sizeof (PORTNAMES);
   ulBeginStruct = 0;

   for (usID = PORT_ID_FIRST;
        usID <= PORT_ID_LAST && ulReturned;
        usID += 2, --ulReturned)
   {
      CopyStruct (hab, hModule, usID, pBuf, &ulBeginStruct, &ulBeginText);
   }
}

/****************************************************************************
 *
 * FUNCTION NAME = CopyStruct
 *
 * DESCRIPTION   = Copy single PORTNAMES structure to buffer
 *
 * INPUT         = hab     - anchor block handle
 *                 hModule - this port driver's module handle
 *                 usID    - string ID for port to return
 *                 pBuf    - buffer to get PORTNAMES structures
 *                 pulBeginStruct - offset from begin of pBuf to store next
 *                                  PORTNAMES
 *                 pulBeginText   - offset from pBuf to store next string
 *
 * OUTPUT        = pBuf is updated
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID CopyStruct ( HAB hab,
                  HMODULE hModule,
                  USHORT usID,
                  PCH pBuf,
                  PULONG pulBeginStruct,
                  PULONG pulBeginText )
{
   PPORTNAMES pPortNames;

   pPortNames = (PPORTNAMES)(pBuf + *pulBeginStruct);
   *pulBeginStruct += sizeof (PORTNAMES);

      /*
      ** copy port name in the structure
      */
   pPortNames->pszPortName = pBuf + *pulBeginText;
   WinLoadString(hab, hModule, usID, STR_LEN_PORTDESC, pPortNames->pszPortName);
   *pulBeginText += strlen (pPortNames->pszPortName) + 1;

      /*
      ** copy port description to the structure
      */
   pPortNames->pszPortDesc = pBuf + *pulBeginText;
   WinLoadString(hab, hModule, usID, STR_LEN_PORTDESC, pPortNames->pszPortDesc);
   *pulBeginText += strlen (pPortNames->pszPortDesc) + 1;
}

/****************************************************************************
 *
 * FUNCTION NAME = GetPortDescription
 *
 * DESCRIPTION   = Get port description from our string resources.
 *
 * INPUT         = hab     - anchor block handle
 *                 hModule - this port driver's module handle
 *                 pszPortName - name of port to get description for
 *                 pszPortDesc - gets port description
 *
 * OUTPUT        = TRUE  - if portname description is found
 *                 FALSE - if not
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL GetPortDescription ( HAB hab,
                          HMODULE hModule,
                          PSZ pszPortName,
                          PSZ pszPortDesc )
{
   USHORT usID;
   CHAR chBuf[STR_LEN_PORTDESC];

   for (usID = PORT_ID_FIRST; usID <= PORT_ID_LAST; usID += 2)
   {
      WinLoadString(hab, hModule, usID, STR_LEN_PORTDESC, chBuf);
      if (!strcmp (pszPortName, chBuf))
      {
         strcpy (pszPortDesc, chBuf);
         return(TRUE);
      }
   }
   return(FALSE);
}

/****************************************************************************
 *
 * FUNCTION NAME = OpenParallPortDlg
 *
 * DESCRIPTION   = Display port settings dialog
 *
 * INPUT         = hab     - anchor block handle
 *                 hModule - this port driver's module handle
 *                 pszPortName - name of port to get description for
 *                 pszAppName  - INI Appname for port( "PM_Portname").
 *
 * OUTPUT        = TRUE  - if port settings changed
 *                 FALSE - if port settings not changed
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

ULONG OpenParallPortDlg ( HAB hab,
                          HMODULE hModule,
                          PSZ pszPortName,
                          PSZ pszAppName )
{
   PARALLDATA ParallData;

   memset (&ParallData, 0, sizeof (PARALLDATA));
   ParallData.hAB = hab;
   ParallData.hModule = hModule;
   ParallData.pszPortName = pszPortName;
   ParallData.pszAppName = pszAppName;

   WinDlgBox  (HWND_DESKTOP,
               HWND_DESKTOP,
               (PFNWP)ParallDlg,
               (HMODULE)hModule,
               IDD_PORTPARALLEL,
               &ParallData);

   return ParallData.lfModified;
}

/****************************************************************************
 *
 * FUNCTION NAME = ParallDlg
 *
 * DESCRIPTION   = Port settings dialog procedure
 *
 * INPUT         = hDlg: HWND
 *                 msg:  message
 *                 mp1:  message parameter
 *                 mp2:     "       "
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

MRESULT EXPENTRY ParallDlg( HWND hDlg,
                            USHORT msg,
                            MPARAM mp1,
                            MPARAM mp2 )
{
    PPARALLDATA pParallData;
    /*
    **ULONG       ulTimeOut;
    **HWND        hwndMenu;
    **HWND        hwndHelpInstance;
    */
    switch (msg) {

        case WM_INITDLG:
            pParallData = (PPARALLDATA)mp2;
            WinSetWindowULong (hDlg, QWL_USER, (ULONG)pParallData);

            WinSendDlgItemMsg (hDlg, IDC_PPA_TIMEOUT,
                                   SPBM_SETLIMITS,
                                   MPFROMP(TIMEOUT_UPPER_LIMIT),
                                   MPFROMP(TIMEOUT_LOWER_LIMIT));

            {
               CHAR szDesc[ STR_LEN_PORTDESC];
               CHAR szTitle[ STR_LEN_TITLE + 1];

               if (PrfQueryProfileString (HINI_SYSTEMPROFILE,
                                          pParallData->pszAppName,
                                          KEY_DESCRIPTION,
                                          NULL,
                                          (PSZ)szDesc,
                                          STR_LEN_PORTDESC))
               {
                   WinSetWindowText (WinWindowFromID (hDlg,
                                                      (USHORT)IDC_PPA_DESC),
                                     szDesc);
                     /*
                     ** full description port name
                     */
                  WinQueryWindowText (hDlg, STR_LEN_TITLE, szTitle);
                  strcat (szTitle, szDesc);
                  WinSetWindowText (hDlg, szTitle);
               }
            }

            GetPortSettings (pParallData);

            WinSendDlgItemMsg (hDlg, IDC_PPA_TIMEOUT,
                                   SPBM_SETCURRENTVALUE,
                                   MPFROMSHORT(pParallData->usOrgTimeOut),
                                   MPFROMSHORT(NULL));
               /*
               ** added setting - new checkbox for share access
               */
            WinSendDlgItemMsg (hDlg, IDC_PPA_SHAREHW,
                                   BM_SETCHECK,
                                   MPFROMSHORT(pParallData->fOrgShareAccess),
                                   MPFROMSHORT(NULL));
               /*
               ** create help hook for helps
               */
            SetHelpStubHook();

            break;

        case WM_COMMAND:
            pParallData = (PPARALLDATA)WinQueryWindowULong (hDlg, QWL_USER);
            switch (SHORT1FROMMP(mp1)) {
               case IDC_OK:
                     /*
                     ** Call function to save any changed port settings
                     */
                  if (SavePortSettings(pParallData, hDlg))
                  {
                     WinDismissDlg(hDlg, 0);
                  }
                  break;

               case IDC_RESET:
                  WinSendDlgItemMsg (hDlg, IDC_PPA_TIMEOUT,
                                     SPBM_SETCURRENTVALUE,
                                    MPFROMSHORT(pParallData->usOrgTimeOut),
                                    MPFROMSHORT(NULL));
                     /*
                     ** added setting new checkbox for share access
                     */
                  WinSendDlgItemMsg (hDlg, IDC_PPA_SHAREHW,
                                    BM_SETCHECK,
                                    MPFROMSHORT(pParallData->fOrgShareAccess),
                                    MPFROMSHORT(NULL));

                  break;

               case IDC_DEFAULT:
                  WinSendDlgItemMsg (hDlg, IDC_PPA_TIMEOUT,
                                     SPBM_SETCURRENTVALUE,
                                    MPFROMSHORT(DEF_TIMEOUT_VALUE),
                                    MPFROMSHORT(NULL));
                     /*
                     ** added setting new checkbox for share access
                     */
                  WinSendDlgItemMsg (hDlg, IDC_PPA_SHAREHW,
                                    BM_SETCHECK,
                                    MPFROMSHORT(FALSE),
                                    MPFROMSHORT(NULL));

                  break;

               case IDC_CANCEL:
                  WinDismissDlg(hDlg, MBID_CANCEL);
                  break;

               default:
                  return(WinDefDlgProc(hDlg, msg, mp1, mp2) );
                  break;
            }
           break;

        case WM_HELP:
            InitializeHelp();
            if (hwndHelp)
            {
               WinSendMsg (hwndHelp, HM_DISPLAY_HELP,
                                         (MPARAM)IDH_DLG_EXTENDED, NULL);
               return (MRESULT)TRUE;
            }
            break;

        case WM_DESTROY:

               /*
               ** if we have a help instance destroy it.
               */
            if (HelpAlreadyInitialized)
            {
               CALLDestroyHelpInstance(hwndHelp);
               hwndHelp = (HWND) NULL;
               HelpAlreadyInitialized = FALSE;
               HelpStubHookIsSet=FALSE;
            }
            ReleaseHelpStubHook();
             break;

        default:
            return(WinDefDlgProc(hDlg, msg, mp1, mp2) );
            break;
    }
    return FALSE;
}

/****************************************************************************
 *
 * FUNCTION NAME = GetPortSettings
 *
 * DESCRIPTION   = Get port Initialization settings from INI file
 *
 * INPUT         = pParallData -> structure containing port name.
 *                                On return this will have port timeout
 *                                and share access flag.
 *
 * OUTPUT        = TRUE  - if INI information found for port
 *                 FALSE - if INI info not found
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL GetPortSettings( PPARALLDATA pParallData )
{
   CHAR szBuf[20];
   PSZ  p;

      /*
      ** Get port timeout value ( "PM_LPT1" "TIMEOUT" "timeout;" )
      */
   if (!(PrfQueryProfileString (HINI_SYSTEMPROFILE,
                                pParallData->pszAppName,
                                KEY_TIMEOUT,
                                NULL,
                                (PSZ)szBuf,
                                20)))
    {
      return FALSE;
    }
    RemoveLeadTrailBlanks ((PSZ)szBuf, (PSZ)szBuf);
    if (p = strchr(szBuf, ';'))
    {
       *p = '\0';
    }
    if (*szBuf)
    {
       pParallData->usOrgTimeOut = (USHORT)atoi (szBuf);
    }
    else
    {
       pParallData->usOrgTimeOut = DEF_TIMEOUT_VALUE;
    }
    pParallData->usSaveTimeOut = pParallData->usOrgTimeOut;

       /*
       ** Get port's share access flag (part of initialization string)
       */
    if ( (PrfQueryProfileString (HINI_SYSTEMPROFILE, pParallData->pszAppName,
                                KEY_INITIALIZATION, NULL,
                                (PSZ)szBuf,
                                20))
        && (szBuf[0] == '1') )
    {
      pParallData->fOrgShareAccess = TRUE ;
      pParallData->fShareAccess    = TRUE ;
    }
    else
    {
      pParallData->fOrgShareAccess = FALSE;
      pParallData->fShareAccess    = FALSE;
    }

    return(TRUE);
}

/****************************************************************************
 *
 * FUNCTION NAME = SavePortSettings
 *
 * DESCRIPTION   = save any changed values for this LPT port in INI file
 *
 * INPUT         = pParallalData -> this port's information
 *                 hDlg          -  handle of dialog window
 *
 * OUTPUT        = TRUE          -  successful
 *                 FALSE         -  PrfWrite failed
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL SavePortSettings( PPARALLDATA pParallData,
                       HWND hDlg )
{
   CHAR szBuf[20];
   PSZ  pszInitialization ;
   ULONG       ulTimeOut;
   HFILE  hFile ;
   ULONG  ActionTaken ;
   USHORT usParamPacket= 0;
   USHORT usDataPacket = 0;
   ULONG  ulReturned1;
   ULONG  ulReturned2;

      /*
      ** Get current status of share access checkbox
      */
   pParallData->fShareAccess =
                SHORT1FROMMP( WinSendDlgItemMsg (hDlg, IDC_PPA_SHAREHW,
                                                 BM_QUERYCHECK,
                                                 MPFROMSHORT(NULL),
                                                 MPFROMSHORT(NULL)) );

   if (pParallData->fShareAccess != pParallData->fOrgShareAccess)
   {

      if (pParallData->fShareAccess)
         pszInitialization = "1;" ;
      else
         pszInitialization = "0;" ;

      pParallData->lfModified = TRUE;

         /*
         ** If user selects to change parallel port access mode,
         **   we must issue IOCtl to PRINT02.SYS to set the
         **   share mode now. (Open port direct, NONSPOOLED!).
         **   It could be possible the user will use programs
         **   that do not print, but still require the parallel
         **   port's hardware registers
         */
      if ( DosOpen(pParallData->pszPortName,     /* port "LPT1"          */
                   &hFile,
                   &ActionTaken,
                   0L,                           /* filesize             */
                   0L,                           /* attribute            */
                   FILE_OPEN|FILE_CREATE,        /* open flags           */

                   OPEN_FLAGS_NONSPOOLED |       /* open mode            */
                   OPEN_FLAGS_FAIL_ON_ERROR |
                   OPEN_ACCESS_WRITEONLY |
                   OPEN_SHARE_DENYNONE,
                   NULL ) == 0)                  /* No EAs               */
      {
            /*
            ** flag byte: bit 0 off -> don't allow more than one process
            **                         to access parallel port at one time
            **                  on  -> allow multiple processes to access
            **                         parallel port simultaneously
            */
         usDataPacket = (USHORT)pParallData->fShareAccess ;

         ulReturned1 = 1 ;             /* length of in/output parameters */
         ulReturned2 = 1 ;

         DosDevIOCtl(
                hFile,                    /* port file handle            */
                0x5,                      /* Category number             */
                0x51,                     /* Function - set share access */
                (PVOID) &usParamPacket,   /* Not needed for IOCTL        */
                sizeof (BYTE),
                &ulReturned1,
                (PVOID) &usDataPacket,    /* 1 = allow multiple access   */
                sizeof (BYTE),
                &ulReturned2);

         DosClose(hFile) ;
      }
         /*
         ** Write initialization string to ini file
         */
      if (!PrfWriteProfileString (HINI_SYSTEMPROFILE,
                                 pParallData->pszAppName,
                                 KEY_INITIALIZATION,
                                 pszInitialization))
       {
          DE ("Error in writing to system ini file");
          return FALSE;
       }
   }


   WinSendDlgItemMsg (hDlg, IDC_PPA_TIMEOUT, SPBM_QUERYVALUE,
                      (MPARAM)&ulTimeOut, (MPARAM)NULL);
   if (pParallData->usSaveTimeOut != (USHORT)ulTimeOut)
    {

      pParallData->usSaveTimeOut = (USHORT)ulTimeOut;
      pParallData->lfModified = TRUE;
      _itoa (pParallData->usSaveTimeOut, szBuf, 10);
      strcat (szBuf, ";");
         /*
         ** Write data for this port in ini file with new format.
         */
      if (!PrfWriteProfileString (HINI_SYSTEMPROFILE,
                                 pParallData->pszAppName,
                                 KEY_TIMEOUT,
                                 szBuf))
       {
          DE ("Error in writing to system init file");
          return FALSE;
       }

    }

   return TRUE;
}

/****************************************************************************
 *
 * FUNCTION NAME = RemoveLeadTrailBlanks
 *
 * DESCRIPTION   = Remove all the leading blanks and all the trailing blanks
 *                 from the source string. The target string contains no lead
 *                 or trail blanks. Source string is not altered.
 *
 * INPUT         = pTarget   - Target string.
 *                 pSource   - Source string.
 *
 * OUTPUT        = Void function.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 *      Note : Function accepts the same pointer for both source and target.
 *             This means that same buffer can be used as input and output.
 *
 ****************************************************************************/

VOID RemoveLeadTrailBlanks ( PCH pTarget,
                             PCH pSource )
{
   for (; *pSource == ' ' || *pSource == '\t' || *pSource == '\n'; pSource++);
   if (!(*pSource))
   {
      *pTarget = '\0';
      return;
   }
   for (; *pSource; *pTarget++ = *pSource++);
   for (pTarget--; *pTarget == ' ' || *pTarget == '\t' || *pTarget == '\n';
                                                                 pTarget--);
   *++pTarget = '\0';

}

/****************************************************************************
 *
 * FUNCTION NAME = DisplayError
 *
 * DESCRIPTION   = Display error having string from the resource file.
 *
 * INPUT         = hwndOwner     - Owner of message box.
 *                                    if NULL, default is last active window.
 *                 usStringID    - ID of string in resource file.
 *                 usWinStyle    - Window style of message box.
 *                                    if NULL, default is MB_OK.
 *
 * OUTPUT        = User-response value returned by WimMessageBox API.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

USHORT DisplayError ( HWND hwndOwner,
                      HMODULE hModule,
                      USHORT usStringID,
                      USHORT usWinStyle )
{
   CHAR pszTitle[256];                       /*  Message-box window title   */
   CHAR pszText[256];                        /*  Message-box window message */
   USHORT usResponse;
   HAB    hAB;
   ULONG  ulLen = 255L;

   hAB = WinQueryAnchorBlock (HWND_DESKTOP);
   WinLoadString (hAB, hModule, PORT_ERR_TITLE, 255, (PSZ)pszTitle);
   WinLoadString (hAB, hModule, usStringID, 255, (PSZ)pszText);
   if (!hwndOwner)
   {
      hwndOwner = WinQueryActiveWindow (HWND_DESKTOP);
   }
   if (!usWinStyle)
   {
      usWinStyle = MB_OK;
   }
   usResponse = WinMessageBox (HWND_DESKTOP, hwndOwner, pszText, pszTitle, 1,
                                                           (ULONG)usWinStyle);
   return (usResponse);

}

/****************************************************************************
 *
 * FUNCTION NAME = DE
 *
 * DESCRIPTION   = Display Error message
 *
 * INPUT         = str  - error message string to display
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID DE ( PCH str )
{
       WinMessageBox( HWND_DESKTOP, HWND_DESKTOP,
                 (PCH)str,
                 (PCH)"Error",
                 1,
                 MB_OKCANCEL | MB_APPLMODAL |
                 MB_MOVEABLE | MB_ICONASTERISK);

}

/*
** Following routines replace Win help manager function calls.
** This is done to avoid automatically loading the help manager
** when the port driver is used.
** DosLoadModule is used to get the help manager function addresses
** and WinHook mechanism is used to get notified of F1 key.
**
** All CallxxHelpxx call equivalent WinxxHelpxx
*/
/****************************************************************************
 *
 * FUNCTION NAME = CALLAssociateHelpInstance
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL APIENTRY CALLAssociateHelpInstance ( HWND hwndHelpInstance,
                                          HWND hwndApp )
{
   ULONG    rc = 0;
   HMODULE  hModule;

   if (!hvHelpmgrModule)
   {
         /*
         ** If there is an error display it and return.
         ** This call should only be made if CreateHelpInstance was called.
         **
         ** This uses an error message from print object and should
         ** be replaced with custom message.
         */
      DosQueryModuleHandle ("WPPRINT", &hModule);
#define STR_BASE                   7000
#define STR_DLL_LOAD_ERROR         (STR_BASE + 36)
      DisplayError (HWND_DESKTOP, hModule, STR_DLL_LOAD_ERROR,
                              MB_OK | MB_APPLMODAL | MB_MOVEABLE);
   }
   else
   {
         /*
         ** Check to see if we have the pointer from a previous call
         */
      if (!pfnWinAssociateHelpInstance)
      {
            /*
            ** Get pointer to the location of the function we want.
            */
         rc = DosQueryProcAddr (hvHelpmgrModule,(ULONG)NULL,
                                     (PSZ)"WIN32ASSOCIATEHELPINSTANCE",
                                     (PFN *)&pfnWinAssociateHelpInstance);
      }
         /*
         ** If no error continue.
         */
      if (!rc )
      {
         rc = (*pfnWinAssociateHelpInstance)(hwndHelpInstance, hwndApp);
            /*
            ** Function returns a bool
            */
         if (rc == TRUE)
            return(TRUE);
      }
   }
   return(FALSE);
}

/****************************************************************************
 *
 * FUNCTION NAME = CALLCreateHelpInstance
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

HWND APIENTRY  CALLCreateHelpInstance ( HAB hab,
                                        PHELPINIT phinitHMInitStructure )
{
   ULONG    rc = 0;
   HWND     hwnd = (HWND)NULLHANDLE;
   HMODULE  hModule;

      /*
      ** Check to see if we already have the handle
      */
   if (!hvHelpmgrModule)
      rc = DosLoadModule((PSZ)NULL, 0,
                         (PSZ)"HELPMGR", (PHMODULE)&hvHelpmgrModule);
   if (rc)
   {
         /*
         ** If there is an error display it and return.
         */
      DosQueryModuleHandle ("WPPRINT", &hModule);
      DisplayError (HWND_DESKTOP, hModule, STR_DLL_LOAD_ERROR,
                              MB_OK | MB_APPLMODAL | MB_MOVEABLE);
   }
   else
   {
      if (!pfnWinCreateHelpInstance)
            /*
            ** Next get pointer to the location of the function we want.
            */
         rc = DosQueryProcAddr (hvHelpmgrModule,(ULONG)NULL,
                                     (PSZ)"WIN32CREATEHELPINSTANCE",
                                     (PFN *)&pfnWinCreateHelpInstance);
         /*
         ** If no error continue.
         */
      if (!rc )
         hwnd = (*pfnWinCreateHelpInstance)(hab, phinitHMInitStructure );

   }
   return(hwnd);
}

/****************************************************************************
 *
 * FUNCTION NAME = CALLDestroyHelpInstance
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL APIENTRY   CALLDestroyHelpInstance ( HWND hwndHelpInstance )
{

   ULONG    rc = 0;
   HMODULE  hModule;

   if (!hvHelpmgrModule)
   {
         /*
         ** If there is an error display it and return.
         */
      DosQueryModuleHandle ("WPPRINT", &hModule);
      DisplayError (HWND_DESKTOP, hModule, STR_DLL_LOAD_ERROR,
                              MB_OK | MB_APPLMODAL | MB_MOVEABLE);
   }
   else
   {
      if (!pfnWinDestroyHelpInstance)
            /*
            ** Next get pointer to the location of the function we want.
            */
         rc = DosQueryProcAddr (hvHelpmgrModule,(ULONG)NULL,
                                     (PSZ)"WIN32DESTROYHELPINSTANCE",
                                     (PFN *)&pfnWinDestroyHelpInstance);
         /*
         ** If no error continue.
         */
      if (!rc )
      {
         rc = (*pfnWinDestroyHelpInstance)(hwndHelpInstance);
            /*
            ** Function returns a bool
            */
         if (rc == TRUE)
         {
           /* DosFreeModule(hvHelpmgrModule); */
            return(TRUE);
         }
      }
   }
   return(FALSE);
}

/****************************************************************************
 *
 * FUNCTION NAME = CALLQueryHelpInstance
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

HWND APIENTRY   CALLQueryHelpInstance ( HWND hwndApp )
{
   ULONG    rc = 0;
   HWND     hwnd = (HWND)NULLHANDLE;
   HMODULE  hModule;

   if (!hvHelpmgrModule)
   {
         /*
         ** If there is an error display it and return.
         */
      DosQueryModuleHandle ("WPPRINT", &hModule);
      DisplayError (HWND_DESKTOP, hModule, STR_DLL_LOAD_ERROR,
                              MB_OK | MB_APPLMODAL | MB_MOVEABLE);
   }
   else
   {
      if (!pfnWinQueryHelpInstance)
            /*
            ** Get pointer to the location of the function we want.
            */
         rc = DosQueryProcAddr (hvHelpmgrModule,(ULONG)NULL,
                                     (PSZ)"WIN32QUERYHELPINSTANCE",
                                     (PFN *)&pfnWinQueryHelpInstance);
         /*
         ** If no error continue.
         */
      if (!rc )
      {
            /*
            ** Make sure that the handle is associated with this instance
            **
            ** Make call
            */
         hwnd = (*pfnWinQueryHelpInstance)( hwndApp);

      }
   }
   return(hwnd);
}

/****************************************************************************
 *
 * FUNCTION NAME = SetHelpStubHook
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

BOOL EXPENTRY SetHelpStubHook()
{
    if(!HelpStubHookIsSet)
    {
        if(WinSetHook(0L, HMQ_CURRENT, HK_HELP, (PFN)HelpStubHook, 0L))
        {
            HelpStubHookIsSet = TRUE;
            return TRUE;
        }
    }
    return FALSE;
}

/****************************************************************************
 *
 * FUNCTION NAME = ReleaseHelpStubHook
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID EXPENTRY ReleaseHelpStubHook()
{
    if(HelpStubHookIsSet)
    {
        WinReleaseHook(0L, HMQ_CURRENT, HK_HELP, (PFN)HelpStubHook, 0L);
        HelpStubHookIsSet = FALSE;
    }
}

/****************************************************************************
 *
 * FUNCTION NAME = HelpStubHook
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

INT EXPENTRY HelpStubHook( HAB AppHAB,
                           USHORT Context,
                           USHORT IdTopic,
                           USHORT IdSubTopic,
                           PRECTL RectLPtr )
{

    InitializeHelp();

    return FALSE;
}

/****************************************************************************
 *
 * FUNCTION NAME = InitializeHelp
 *
 * DESCRIPTION   =
 *
 * INPUT         =
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

VOID EXPENTRY InitializeHelp()
{
    HAB     hAB;
    HWND    hWnd;
    HWND    hWndActive;
    ULONG   ulBootDrive;
    HMODULE hModule;
    CHAR    szBuf[256];
    CHAR    szPathName[260]; /* will contain full path to this port driver */

    if(HelpAlreadyInitialized)   return;

       /*
       ** Initialize Help
       ** ---------------
       **
       ** Create an instance of the Help Manager, and associate it
       ** with the Frame.  If the Association fails, we handle it
       ** the same way as if the creation fails, ie hwndHelp
       ** (the Help Manager Object Window handle) is set to NULL.
       ** If we can't load the Module containing the Help Panel
       ** definitions, we forget Help altogether.
       */
    hWndActive = WinQueryActiveWindow(HWND_DESKTOP);
    hWnd = WinQueryWindow(hWndActive,QW_OWNER);
       /*
       ** if unable to get active window's owner
       **    use active window
       */
    if (hWnd == (HWND)NULL)
       hWnd = hWndActive ;

       /*
       ** We need our module handle.
       ** Easiest way to do this is to find the path to our port driver
       **  from the system INI file.
       ** If not found in the INI file
       **  assume it is in the boot drive's \OS2\DLL directory
       */

    /* change ? in szDefaultPortDrvPath to boot drive */
    DosQuerySysInfo (QSV_BOOT_DRIVE, QSV_BOOT_DRIVE, &ulBootDrive,
                    sizeof (ULONG));
    szDefaultPortDrvPath[0] = (CHAR)(ulBootDrive + 'A' - 1);

    PrfQueryProfileString (HINI_SYSTEMPROFILE,
                          "PM_PORT_DRIVER",
                          "PARALLEL",
                          szDefaultPortDrvPath,
                          szPathName,
                          256 );
       /*
       ** get module handle for our dll
       */
    DosQueryModuleHandle ( szPathName, &hModule);

       /*
       ** Initialize a couple of the helpmgr structure elements
       ** First, get the title
       **
       ** Now load the title
       */
    WinLoadString (hAB, hModule,
                   (USHORT)(PORT_HELP_TITLE), (SHORT)(256), szBuf);

    hmiHelpData.pszHelpWindowTitle = (PSZ)szBuf;
    hmiHelpData.pszHelpLibraryName = "WPHELP.HLP";

    hAB = WinQueryAnchorBlock(hWnd);

       /*
       ** Only create a handle if we don't have one.
       */
    if (hwndHelp == 0L)
       hwndHelp = CALLCreateHelpInstance(hAB, &hmiHelpData);

       /*
       ** Always associate the helpmgr handle with the active window
       */
    if (hwndHelp != 0L)
    {
       if(!CALLAssociateHelpInstance(hwndHelp, hWnd) )
       {
           CALLDestroyHelpInstance(hwndHelp);
           hwndHelp = (HWND)NULL;
       }
    }

       /*
       ** If help was initialized, get rid of our hook. Otherwise, we have
       ** to ensure that our stub hook is the FIRST hook in the HK_HELP
       ** hook chain.
       */
    if(hwndHelp != 0L)
    {
        HelpAlreadyInitialized = TRUE;
        ReleaseHelpStubHook();
    }
    else
    {
        ReleaseHelpStubHook();
        SetHelpStubHook();
    }
}
