/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Microsoft Corporation, 1989                                 */
/* 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 = DEVMODE.C
 *
 * DESCRIPTIVE NAME = Contains OS2_PM_DRV_DEVMODE.
 *
 *
 * VERSION = V2.0
 *
 * DATE      09/16/89
 *
 * DESCRIPTION  This file contains OS2_PM_DRV_DEVMODE.
 *
 *
 * FUNCTIONS
 *                 OS2_PM_DRV_DEVMODE
 *                 CheckPrinterName
 *                 GetPrinterName
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *      extern HMODULE hModPS;
 *      extern PBYTE StringTable[MAX_STRINGS];
 *
 * EXTERNAL FUNCTIONS
 *      extern ULONG   GplErrSetError;
 *      extern BOOL   ValidateDriveData(PLDRIVDATA,PCNFDATA);
 *
*/
#define  INCL_DEV
#define  INCL_WINERRORS
#include <string.h>
#include <stdlib.h>
#include "inc\prdinclt.h"
#include "inc\prdgextf.h"
#include "inc\prdmath.h"
#include "inc\prdrsrc.h"
#include "inc\utl.h"
#include "inc\config.h"
#include "inc\pspagtun.h"              /* V2.174057   Page Tuning */
#define  INCL_GENPLIB_ERROR
#define  INCL_GENPLIB_MEMORY
#include <genplib.h>

#define  INCL_WINSHELLDATA
#define  INCL_INCL_WINSHELLDATA
#include <pmshl.h>

extern HMODULE pscript_module;                 /* memory.c */
extern PBYTE StringTable[MAX_STRINGS]; /* memory.c */
extern PVOID pProcessHeap;
extern BOOL   ValidateDriveData(PLDRIVDATA,PCNFDATA);
/*            */
extern LONG ProfileAllocStringQuery( HINI, PSZ, PSZ, PSZ, PPVOID );

static PSZ pm_spooler_printer = "PM_SPOOLER_PRINTER";
SHORT ValidateSelector(SHORT);
SHORT DeviceModes(PSZ,PCNFDATA,SHORT);
void   szNewCopy(PSZ,PSZ,SHORT);
BOOL   sznIsEqual(PSZ,PSZ);
BOOL   GetPrinterName(PSZ pszDevice,PSZ pszPName);

/***************************************************************************
 *
 * FUNCTION NAME = OS2_PM_DRV_DEVMODE
 *
 * DESCRIPTION   = The DevPostDeviceModes function causes a device driver to
 *                 post a dialog box so the user can set options for the device
 *                 (resolution, font cartridges, and so on).
 *
 *                 The application can call this function first with a NULL data
 *                 pointer to find how much storage is needed for the data
 *                 buffer.  It then calls the function a second time to have the
 *                 buffer filled with data.  You can then pass the returned data
 *                 to the DevOpenDC function as the buffer data pointed to by
 *                 the pbDriverData parameter.
 *
 *                 Parameter      Description
 *                 --------------------------------------------
 *                 hab
 *                 Identifies the anchor block.
 *
 *                 pbDriverData
 *                 Points to the data buffer that receives device data defined
 *                 by the driver. If this parameter is NULL, the function
 *                 returns the required buffer size. The format of the data is
 *                 the same as for the pbData parameter of the DevOpenDC
 *                 function.
 *
 *                 pszDriverName
 *                 Points to the null-terminated string that contains the name
 *                 of the device driver.
 *
 *                 achDeviceName
 *                 Points to the null-terminated string that identifies the
 *                 particular device (for example, its model number). This
 *                 string must not exceed 32 bytes. Valid names are defined by
 *                 device drivers.
 *
 *                 pszName
 *                 Points to the null-terminated string that contains the
 *                 printer name.
 *
 *                 flOptions
 *                 Specifies whether the function should display a dialog box
 *                 that allows the user to change job properties, display two
 *                 dialog boxes that allow the user to change job and printer
 *                 properties, or simply return the current job properties. This
 *                 parameter can be one of the following values:
 *
 *                 Value               Meaning
 *                 -------------------------------------------------------------
 *                 DPDMF_POSTJOBPROP   Display a dialog box that allows the user
 *                                     to change job properties. The default
 *                                     values for this dialog box are taken from
 *                                     the PM_SPOOLER_DD section of the os2.ini
 *                                     file if the pszName parameter specifies a
 *                                     logical address. If pszName is NULL, the
 *                                     default values are taken from the
 *                                     pbDriverData parameter.
 *
 *                 DPDMF_CHANGEPROP    Display two dialog boxes. The first
 *                                     dialog box allows the user to change job
 *                                     properties; the second allows the user to
 *                                     change printer properties. The default
 *                                     values for these dialog boxes are taken
 *                                     from the PM_SPOOLER_DD section of the
 *                                     os2.ini file. The function returns the
 *                                     new values in the pbDriverData parameter.
 *                                     The pszName parameter cannot be NULL when
 *                                     this option is selected.
 *
 *                 DPDMF_QUERYJOBPROP  Return the current job properties.
 *
 * INPUT         = (pDriverData,pszDriver,pszDevice,pszName,options)
 *
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 *                 The return value, if the pbDriverData parameter is NULL, is
 *                 the size (in bytes) required for the data buffer, DPDM_NONE
 *                 if there are no settable options, or DPDM_ERROR if an error
 *                 occurs.
 *
 *                 The return value, if pbDriverData is not NULL, is DEV_OK if
 *                 the function is successful, DPDM_NONE if there is no device
 *                 mode, or DPDM_ERROR if an error occurs.
 *
 * RETURN-ERROR  = NONE
 *
 *                 Use the WinGetLastError function to retrieve the error value,
 *                 which may be one of the following:
 *
 *                 PMERR_INV_DEVICE_NAME
 *                 PMERR_INV_DRIVER_DATA
 *                 PMERR_INV_DRIVER_NAME
 *                 PMERR_INV_LOGICAL_ADDRESS
 *
 *
 **************************************************************************/

ULONG OS2_PM_DRV_DEVMODE( PLDRIVDATA pDriverData, PSZ pszDriver, PSZ pszDevice, PSZ pszName, ULONG options )

  /* PLDRIVDATA pDriverData;               pointer to drivdata block         */
  /* PSZ pszDriver;                        pointer to driver name            */
  /* PSZ pszDevice;                        pointer to device name under      */
  /*                                       which printer is registered       */
  /* PSZ pszName;                          error log address                 */
  /* ULONG options;                        save options                      */

  /* HAB hab;                      anchor-block handle                         */
  /* PDRIVDATA pbDriverData;       pointer to buffer for data                  */
  /* PSZ pszDriverName;            pointer to string for driver name           */
  /* PSZ achDeviceName;            pointer to device name                      */
  /* PSZ pszName;                  pointer to string for output device name    */
  /* ULONG flOptions;              specifies various options                   */

{
  #define  NAMELEN       32

  SHORT     i;
  CNFDATA  *pcnfData;
  SHORT     DataSelector, SelectorLimit;
  BOOL      bSamePrinterType;
  PSZ       pszPointer;
  CHAR      achPName[NAMELEN+1];

  /*
  ** if the pointer to the drivedata is NULL, we are to simply return
  ** the size of our drivedata structure.
  */
  if (pDriverData == (PLDRIVDATA) NULL)
  {
    return ((ULONG) sizeof(LDRIVDATA));
  }

  /*
  ** validate the selector.  ie make sure it exists and is large enough
  ** to handle our drivedata structure.
  ** if the DPDMF_CHANGEPROP option is selected, it is an error if
  ** pszName == NULL.
  */
  if ((options == DPDM_CHANGEPROP) && (pszName == NULL))
  {
    RIP( "OS2_PM_DRV_DEVMODE: pszName == NULL when DPDMF_CHANGEPROP" );
    GplErrSetError(  PMERR_INV_DRIVER_NAME );
    return( DPDM_ERROR );

  }

  /*
  ** create the driver's heap and initialize stuff.
  */
/*if (!InitGlobalHeap ())
  {
    return( DPDM_ERROR );
  } */

  /*
  ** allocate space for our configuration data block.
  */
  pcnfData = (CNFDATA  *)GplMemoryAlloc( pProcessHeap, sizeof(CNFDATA) );

  if (pcnfData == NULL)
  {
    return( DPDM_ERROR );
  }

  /*
  ** copy driverdata to configuration data block.
  */
/* *pcnfData = pDriverData->cnfData; */
  memcpy( pcnfData, &(pDriverData->cnfData), sizeof(CNFDATA) );

  pcnfData->u.iv.pSourcePaper = (PSOURCE)GplMemoryAlloc( pProcessHeap,
                                                         sizeof( SOURCE ) );

  /*
  ** CR!!! There is a     in the 1.2 and 1.3 loader where if multiple threads
  ** try to load a driver at the same time the first thread will begin
  ** doing the driver load time initialization and the second thread
  ** will then enter the driver and but not wait for initialization to
  ** be complete.      in LoadModule somewhere.  So to prevent threads
  ** from go through until initialized we loop until initialization is
  ** complete
  */
  while (!(pcnfData->hmod = pscript_module));

  /*
  ** if we are given a device name, save it as the key on which we will
  ** be storing our information into os2sys.ini.  if no device name
  ** was given, default to the generic laser printer.
  */
  if ((pszDevice != NULL) && (*pszDevice != '\0'))
  {
    /*
    ** See if same printer type - if it's not we will not use the properties
    */
    bSamePrinterType = szIsEqual((PSZ) pcnfData->szSegName, (PSZ) pszDevice );
    pszPointer = pszDevice;
  }

  else
  {
    bSamePrinterType = FALSE;
    pszPointer = StringTable[IDS_Generic-STRING_BASE];
  }


  /*            */

  /*
  **  put the printer type in the two fields below
  */
  szNewCopy((PSZ) pcnfData->szPrtName, pszPointer, sizeof(pcnfData->szPrtName));
  szNewCopy((PSZ) pcnfData->szSegName, pszPointer, sizeof(pcnfData->szSegName));

  /*            */
  /*
  ** build keyapp form of PM_DD_<printerName>,<deviceDriver>.<deviceName>
  */
  /*            */
  /*
  ** If the name is null try to find a printer of same type
  ** If no printer name given use devicename preceded by a blank and .
  */
  if (!pszName || !*pszName)
  {
    /*
    ** Try to find printer
    */
    if (GetPrinterName( pszDevice, (PSZ) achPName) == TRUE)
    {
      /*
      ** Set name to point to rite name
      */
      pszName = (PSZ) achPName;
    }
    else
    {
      pszName = NULL;                  /* Kill pointer */
      strcpy( pcnfData->szKeyApp, " ." );
      strcat( pcnfData->szKeyApp, pszPointer );
    }
  }

  if (pszName != NULL)
  {                           /* Printer names limited to total of 32 chars */
    strcpy( pcnfData->szKeyApp, "PM_DD_" );
    szNewCopy( (PSZ) pcnfData->szKeyApp+6, (PSZ) pszName, NAMELEN );
    strcat( pcnfData->szKeyApp, ",PSCRIPT." ); /* Add in driver name */

    /*
    ** Tack on device name (< 32)
    */
    szNewCopy( pcnfData->szKeyApp+strlen(pcnfData->szKeyApp), (PSZ) pszPointer,
               NAMELEN );
  }

  /*
  ** this seems hokey, but this is what we have to do to work correctly
  ** with the print manager.  they call us with the drive data zeroed out;
  ** including the version number.  in this case, they expect us to fill
  ** in the drive data with some "default" set of information.
  */
  if (ValidateDriveData( (PLDRIVDATA) pDriverData, pcnfData) == FALSE)
  {
    pcnfData->lVersion = -1;    /* Signal     data for later use              */
    pcnfData->iDestnType = SYSTEM;
    pcnfData->jobProperties.uScale = (SHORT) DEFAULT_SCALE;
    pcnfData->jobProperties.iOrient = (SHORT) DEFAULT_ORIENT;
    pcnfData->jobProperties.iManualfeed = (SHORT) DEFAULT_MANUAL;
    pcnfData->jobProperties.fIsColorDevice = (BOOL) DEFAULT_COLOR;
    pcnfData->effOutput.iJobTimeout = (SHORT) DEFAULT_TIMEOUTS;
    pcnfData->effOutput.iWaitTimeout = (SHORT) DEFAULT_TIMEOUTS;
    pcnfData->effOutput.fIsFliptb = (BOOL) DEFAULT_TOPBOTTOM;
    pcnfData->effOutput.fIsFliplr = (BOOL) DEFAULT_LEFTRIGHT;
    pcnfData->effOutput.fIsDrawInverted = (BOOL) DEFAULT_INVERT;
    pcnfData->iCntCopies = (SHORT) DEFAULT_COPIES;
    pcnfData->szDestnFile[0] = '\0';
    pcnfData->sDuplexMode = (SHORT) DUPLEX_NONE; /*            */
    pcnfData->uResolution = 0;      /*            unknown */
    pcnfData->sUseDLFonts = 1;      /*            */
    pcnfData->usPSLevel1  = 0;      /*            */

    /*
    ** @V3.0GAMMA1
    */
    pcnfData->lGammaValue = NO_GAMMA;
  }

   /*            */
   /*
   ** If the driverData is not null and it's a PostJobProperies request
   ** then get data/properties from driverData (vs .ini)
   */
   /*            */
   /*
   **  make sure job props are OK
   */
  if ((pDriverData != NULL) && (options == DPDM_POSTJOBPROP) &&
     bSamePrinterType && (pcnfData->lVersion > 0))
  {
    options = DPDM_USERPROPERTIES;
  }

  /*
  ** fill in some fields in the printer configuration data structure.
  */
  pcnfData->uSaveOptions = (SHORT) options;

  /*
  ** call the routine which does the work.
  */
  i = DeviceModes(pszDevice, (PCNFDATA) pcnfData, (SHORT) options );
  pDriverData->lVersion = pcnfData->lVersion = DRIVERSION; /*            */
  pDriverData->cb = sizeof( LDRIVDATA );

  if (pszDevice != NULL)
  {
    szNewCopy( (PSZ) pDriverData->szDeviceName, (PSZ) pszDevice, sizeof
       (pDriverData->szDeviceName) );
  }
  else
  {
    szNewCopy( (PSZ) pDriverData->szDeviceName, (PSZ) StringTable[IDS_Generic -
       STRING_BASE], sizeof(pDriverData->szDeviceName) );
  }
  pDriverData->cnfData = *pcnfData;    /* pass all the configuration data
                                          back                              */
  pDriverData->cnfData.u.iv.pSourcePaper = NULL;
  /*
  ** free up allocated resources.
  */
  GplMemoryFree( pcnfData->u.iv.pSourcePaper );
  GplMemoryFree((PB)pcnfData );

  /*
  ** delete the driver's heap and initialize stuff.
  **  Don't kill the heap if the driver is going to unload; these resources
  **  will be freed automatically, because if another thread from DPDM is using
  **  the heap freeing the heap would cause the second thread to GP.
  **  KillGlobalHeap( );
  */
  return( (ULONG) i );
}

/*            */
/***************************************************************************
 *
 * FUNCTION NAME = CheckPrinterName
 *
 * DESCRIPTION   = Given logical name of printer will see if it has any pscript printers
 *                 of the requested type.
 *
 * INPUT         = pszPSName, pszPrinter, fDefault
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE if match
 *
 * RETURN-ERROR  = False if not
 *
 **************************************************************************/

BOOL CheckPrinterName( PSZ pszPSName, PSZ pszPrinter, BOOL fDefault )
{
  /* CHAR           achBuf[256]; */
  PSZ            achBuf;               /*            */
  register SHORT i;
  PSZ            p;

  /*
  ** Get the data for printer - in form of "port;device[,device];queue"
  */
  /*
  ** PrfQueryProfileString( HINI_SYSTEMPROFILE, pm_spooler_printer, pszPrinter,
  **    ";;;", (PSZ) achBuf, (ULONG) sizeof(achBuf) );
  */
  /*            */
  ProfileAllocStringQuery( HINI_SYSTEMPROFILE, pm_spooler_printer, pszPrinter,
                           ";;;", (PPVOID) &achBuf );

  /*
  ** If not attached to port return
  */
  /*            */
  /*
  ** Network printers dont have a port so don't check if default
  */
  if ((fDefault == FALSE) && (achBuf[0] == ';'))
  {
    /*            */
    GplMemoryFree( achBuf );
    return( FALSE );
  }
  p = achBuf;

  while (*p != ';')                    /* Skip over port */
  {
    p++;
  }
  p++;                                 /* And the colon */

  /*
  ** Loop for each name in device list
  */
  while (*p != ';')
  {
    /*
    ** Compare device to parm
    */
    i = 0;

    while (*(pszPSName+i) == *p)
    {
      i++;
      p++;
    }

    /*
    ** Good ending conditions
    */
    if (*(pszPSName+i) == '\0' && (*p == ',' || *p == ';'))
    {
      /*            */
      GplMemoryFree( achBuf );
      return( TRUE );
    }

    /*
    ** No match - see any more device names
    */
    while (*p != ',' && *p != ';')
    {
      p++;
    }

    if (*p == ',')                     /* Comma means more to come */
    {
      p++;
    }
  }

  /*            */
  GplMemoryFree( achBuf );

  return( FALSE );
}

/*            */
/*            */
/***************************************************************************
 *
 * FUNCTION NAME = GetPrinterName
 *
 * DESCRIPTION   = First checks the default printer, then all others for the
 *                 first match of requested name.
 *
 *                 pszDevice = the device type to look for eg. QMS-PS 810
 *                 pszPname  = pointer to buffer for printer name eg PRINTER1
 *
 * INPUT         = pszDevice, pszPName
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = TRUE if found and copies name into supplied buffer.
 *
 * RETURN-ERROR  = FALSE if no match
 *
 **************************************************************************/

BOOL GetPrinterName( PSZ pszDevice, PSZ pszPName )
{
  /* CHAR           achPrt[256]; */
  PSZ            achPrt;              /*            */
  CHAR           achTypeDotName[66];
  PSZ            p;
  register SHORT i;
  BOOL           RC = FALSE;

  /*
  ** Build name as PSCRIPT.Printer_name
  */
  strcpy( achTypeDotName, "PSCRIPT." );
  strcpy( &achTypeDotName[8], pszDevice );

  /*
  ** see if default printer set up
  */
  /*
  ** if ((i = (SHORT) PrfQueryProfileString( HINI_PROFILE, "PM_SPOOLER", "PRINTER",
  **    "", (PSZ) achPrt, (ULONG) sizeof(achPrt))) > 1)
  */
  /*            */
  if ((i = (SHORT) ProfileAllocStringQuery( HINI_PROFILE, "PM_SPOOLER",
                   "PRINTER", NULL, (PPVOID) &achPrt)) > 1)
  {                                    /* Yes - see if it's the right type */
    /*
    ** Strip off trailing semicolon
    */
    achPrt[i-2] = '\0';
    RC = CheckPrinterName( (PSZ) achTypeDotName, (PSZ) achPrt, TRUE );
    i = 0;                             /* Set for copy to name later */
  }

  if (RC == FALSE)
  {
    /*
    ** Get enum list of printers
    */
    /*
    ** PrfQueryProfileString( HINI_SYSTEMPROFILE, pm_spooler_printer, (PSZ) NULL,
    **    "", (PSZ) achPrt, (ULONG) sizeof(achPrt) );
    */
    /*            */
    ProfileAllocStringQuery( HINI_SYSTEMPROFILE, pm_spooler_printer, (PSZ) 0,
                             NULL, (PPVOID) &achPrt );
    i = 0;

    while (i < sizeof(achPrt) && achPrt[i] != '\0')
    {
      if ((RC = CheckPrinterName( (PSZ) achTypeDotName, (PSZ) &achPrt[i], FALSE))
         == TRUE)
      {
        break;                         /* match found, break loop */
      }

      /*
      ** Skip to next name
      */
      while (achPrt[i++] != '\0');
    }
  }

  if (RC == TRUE)
  {                                    /* Copy name */
    /*            */
    strcpy( pszPName, (char *) (achPrt + i) );
    GplMemoryFree( achPrt );

    return( TRUE );
  }
  else
  {
    /*            */
    GplMemoryFree( achPrt );

    return( FALSE );
  }
}
