/*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.                                */
/*                                                                           */
/*****************************************************************************/
#pragma  pagesize(55)

/**************************************************************************
 *
 * SOURCE FILE NAME = DOSIO.C
 *
 * DESCRIPTIVE NAME = PLOTTER DRIVER SOURCE
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION PLOTTER DRIVER SOURCE FILE-This file contains the high level
 *             output functions which comunicate with the output thread code
 *             in thread.c to output data to a file or device
 *
 *
 * FUNCTIONS   close_file_or_device All done,  so close the output device
 *
 *             get_ieee_addr        Returns the IEEE address from argument.
 *
 *             get_ini_comm_info    Extract com port modes from the
 *                                  os2sys.ini file.
 *
 *             initialize_comm_port Set the comm port to the conditions
 *                                  determined from the os2.ini file
 *
 *             open_file_or_device  Open the file name passed to us.  This
 *
 *             open_spooler_queue   Output is destined for the spooler,
 *
 *             output_bytes         write data to the output.
 *
 *             perform_std_spooling writedata to the spooler.
 *
 *
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_WINSHELLDATA
#define  INCL_WINMESSAGEMGR            /* Message management- winmsgsemwait */
#define  INCL_DOSSEMAPHORES            /* Dos semaphores                    */
#define  INCL_SPL                      /* Spooler stuff                     */
#define  INCL_DOS                      /* Dos stuff                         */
#define  INCL_SPLFSE                   /* File System Interface             */
#define  INCL_DOSERRORS                /* OS/2 base Errors                  */
#define  INCL_DOSMEMMGR                /* Memory management                 */
#include "plotters.h"
#include "dialog.h"
#include "error.h"
#include "utils.h"
#include "init.h"
#include "output.h"
#include "dosio.h"

/*
** Local Structures
*/

typedef struct
{
  USHORT usBaudRate;                   /* Numerical value                   */
  LINECONTROL lcData;                  /* Parity, stop bits, data bits      */
  DCBINFO DCBInfo;                     /* Handshake + write timeout         */
} COMMINFO;

/*
** Local Defines
*/

typedef COMMINFO  *PCOMMINFO;

/*
** static data
*/
PSZ vpszAbortString = "\003\033.J\033.K";

#ifdef   DEBUG

/*
**        The following allows duplicating the data sent to the plotter
**  onto a file,  for later analysis.  To start this,  use the debugger
**  to set iDebugGo to non-zero.
*/

static HFILE hfDebug = 0;
INT         iDebugGo = 1;             /* Set to != 0 (with debugger) for
                                          use                               */
#endif

/*
** Local Functions definitions
*/

#if      IEEESUPPORT
LOCAL SHORT  get_ieee_addr(PSZ);
#endif

LOCAL BOOL   get_ini_comm_info(PPDEVICE,PCOMMINFO);
LOCAL BOOL   initialize_comm_port(PPDEVICE);

/*
** Local Thread functions
*/


/***************************************************************************
 *
 * FUNCTION NAME = close_file_or_device
 *
 *
 * DESCRIPTION   = All done,  so close the output device
 *
 *
 * INPUT         = PDDC        pDDC
 *                 PPDEVICE    pPDevice
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = DosClose() return code
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

USHORT  close_file_or_device(PDDC pDDC,PPDEVICE pPDevice)
{
  USHORT usResult;

#if      IEEESUPPORT

  if (pPDevice->IEEEdevice)
  {
    usResult = IeeeClose(pPDevice->hIEEE);
    pPDevice->IEEEdevice = 0;
  }

  else
#endif
    if (pPDevice->hDev)
    {
      GplThreadEnd(pPDevice->hThread); /* End t2-thread stuff               */
      usResult = PrtClose(pPDevice->hDev);
      pPDevice->hDev = 0;
    }

#ifdef   DEBUG
  if (iDebugGo && hfDebug)
  {
    DosClose(hfDebug);
    hfDebug = 0;                       /* No longer valid                   */
  }
#endif

  return  usResult;
}

#if      IEEESUPPORT

/***************************************************************************
 *
 * FUNCTION NAME = get_ieee_addr
 *
 *
 * DESCRIPTION   = Returns the IEEE address from argument.
 *
 *
 *
 *
 * INPUT         = pAddr
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL =   IEEE address - ok
 * RETURN-ERROR  =   5            - failed somehow or other
 *
 *
 *
 **************************************************************************/

LOCAL SHORT  get_ieee_addr(PSZ pAddr)
{
  SHORT Addr = 5;

  str_to_int(pAddr+4, &Addr);
  return  Addr;
}

#endif                                 /* IEEESUPPORT                       */

/***************************************************************************
 *
 * FUNCTION NAME = get_ini_comm_info
 *
 * DESCRIPTION   = Extract com port modes from the os2sys.ini file.
 *
 *
 *
 * INPUT         = pPDevice,pCommInfo
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL =  TRUE  - User's structure has been initialised
 * RETURN-ERROR  =  FALSE - Structure not filled in
 *
 *
 *
 **************************************************************************/

LOCAL BOOL  get_ini_comm_info(PPDEVICE pPDevice,PCOMMINFO pCommInfo
)
{
  SHORT Plotter = pPDevice->Plotter;   // @980

  /*
  **
  **   We attempt to  read the  comm port  settings from  the OS2SYS.INI
  **   file and fill a private structure containing the information found.
  **
  **     FALSE is returned if an error is detected.
  **
  */

  BOOL bResult = FALSE;
  ULONG IniSize = 0;
  USHORT Count;
  PSZ pIniStr = 0L;
  PSZ pStr;
  USHORT fsMediaSize;

  /*
  ** Get port details
  */
  if ((PrfQueryProfileSize(HINI_SYSTEMPROFILE,
                           "PM_SPOOLER_PORT",
                           pPDevice->pszLogAddress, &IniSize)) &&
                           (pIniStr = (PSZ)GplMemoryAlloc (pPDevice->hmcbHeap,
                                                            IniSize+1)) &&
                           (PrfQueryProfileString (HINI_SYSTEMPROFILE,
                                                   "PM_SPOOLER_PORT",
                                                   pPDevice->pszLogAddress,
                                                   ";;;;;",
                                                   pIniStr,
                                                   IniSize)))
  {

  /*
  ** Requires 5 semicolons to make it acceptable for us.  Format of an
  ** OS2SYS.INI entry for a COM port is:
  **
  **     baud rate;parity;word length;stop bits;handshaking;
  **
  */

    for (pStr = pIniStr, Count = 0; *pStr && Count < 5; )
    {
      if (*pStr++ == ';')
        ++Count;
    }

    if (Count >= 5)
    {
      pStr = pIniStr;

      /*
      **  Baud rate
      */
      if (*pStr == ';')
        pCommInfo->usBaudRate = 1200;  // :default 1200
      else
      {
        pCommInfo->usBaudRate = 0;

        while (*pStr != ';')
        {
          pCommInfo->usBaudRate = pCommInfo->usBaudRate *10+*pStr++-'0';
        }
      }

      /*
      ** Parity
      */
      if (*(++pStr) == ';')
        pCommInfo->lcData.bParity = 0; // : default no parity
      else
        pCommInfo->lcData.bParity = (BYTE)((CHAR)*pStr++-'0');

      /*
      ** Word Length
      */
      if (*(++pStr) == ';')
        pCommInfo->lcData.bDataBits = 8; // : default 8 bits/byte
      else
        pCommInfo->lcData.bDataBits = (BYTE)((CHAR)*pStr++-'0');

      /*
      ** Stop Bits
      */
      if (*(++pStr) == ';')
        pCommInfo->lcData.bStopBits = 0; // : default 1 stop bit
      else
      {
        if (pStr[1] == '.')
        {
          pCommInfo->lcData.bStopBits = 1; // : 1.5 bits
          pStr += 3;
        }
        else
        {
          if (*pStr++ == '1')
            pCommInfo->lcData.bStopBits = 0;
          else
            pCommInfo->lcData.bStopBits = 2;
        }
      }

      /*
      ** Get the hardware handshaking
      */
      if (!PrtDevIOCtl(&pCommInfo->DCBInfo, 0L, 0x73, 1,
                       pPDevice->hDev))
      {
        /*
        ** Output handshaking is controlled by bits 3, 4 and 5 of
        ** control block flags1, and bit 0 of flags2.  If the next
        ** entry in the INI buffer is a semicolon or '0', then
        ** hardware handshaking is disabled.  If it is '1', then
        ** hardware handshaking is enabled.
        **
        */
        if (*(++pStr) == ';' || *pStr == '0')
        {                              /* Not hardware                      */
          /*
          ** The control panel does not give us the
          ** option of software handshake, so this should really
          ** default to XON/XOFF rather than none - none is
          ** nonsensical for a plotter!
          */
          pCommInfo->DCBInfo.fbCtlHndShake &= 0xc7;
          pCommInfo->DCBInfo.fbFlowReplace |= 0x01;
          pPDevice->HandShake = HNDSHK_NONE;
        }
        else
        {
          /*
          ** use hardware handshake
          **
          ** the following code generates a conversion
          ** between type error.
          */
          pCommInfo->DCBInfo.fbCtlHndShake =
                           (pCommInfo->DCBInfo.fbCtlHndShake & 0xDF) | 0x18;
          pCommInfo->DCBInfo.fbFlowReplace &= 0xfe;
          pPDevice->HandShake = HNDSHK_HARD;
        }

        /*
        ** The following timeout is a guess.  10s seemed long
        ** enough, so I figure 1 minute is plenty.  HOWEVER,
        ** there are times when this will be too short, and the
        ** user will need to RETRY.  This value is a trade off
        ** between how long the system (e.g.  the spooler) will
        ** hang around waiting to stop on error, vs false
        ** alarms to the user.  It ought to be calculated in a
        ** more sophisticated way, e.g.  by the driver
        ** determining how long each instruction will take, and
        ** allowing 50% more than this for the plotter to
        ** complete the task.  I am assuming that the plotter
        ** is slower than the serial link.
        **
        ** Eric B.  varied the
        ** timeout with the paper size for b716533 where the
        ** Abort, Retry, Ignore was appearing too often
        */
        fsMediaSize = 1 << pPDevice->pSetup->Size;

        /*                                                              */
        /*
        ** PTR B719978
        **
        ** The first clause is the original ERICB fix for PTR
        ** 716533, untouched.
        **
        ** The "else" clause incorporates the fix for PTR
        ** b717980 [            AND B719978.
        **
        ** THE ORIGINAL FIX FOR PB717980 WAS TO BUMP THE TIMEOUT
        ** VALUE FOR ALL PAPER SIZES ON THE 7374 AND 7375X PLOT-
        ** TERS TO A MINIMUM OF 18000.  THIS WAS BASICALLY A 3X
        ** IN THE TIMEOUT VALUE OVER OTHER PLOTTERS.  THE FIX
        ** FOR B720143 SIMPLY CARRIED THIS INCREASE INTO ALL BUT
        ** THE LARGEST PAPER SIZES (24 MINUTES IS A BIT EXTREME)
        ** WHICH WAS INCREASED BY 33 PERCENT
        **
        ** ON FIRST EXAMINATION, THESE INCREASES MAY SEEM A BIT
        ** EXTREME, BUT HOPEFULLY THEY WILL ENSURE THAT THIS
        ** PTR WILL NOT ARISE AGAIN.
        **
        ** 28FEB1991  DLR.
        */
        if (!(IsIBM7374(Plotter) || IsIBM7375X(Plotter)))
        {
          if (fsMediaSize&(MEDSZ_C|MEDSZ_A2))
            /*
            ** C (17 x 22 in.)
            ** A2 (420 x 594 mm)
            **
            ** approx. 90 sec
            */
            pCommInfo->DCBInfo.usWriteTimeout = 9000;
          else
            if (fsMediaSize&(MEDSZ_D|MEDSZ_A1|MEDSZ_R24))
              /*
              ** D (22 x 34 in.)
              ** A1 (594 x 841 mm)
              ** Roll (24 x ??? in.)
              **
              ** approx. 2 min
              */
              pCommInfo->DCBInfo.usWriteTimeout = 12000;
            else
              if (fsMediaSize&(MEDSZ_E|MEDSZ_A0|MEDSZ_R36))
                /*
                ** E (34 x 44 in.)
                ** A0 (841 x 1189 mm)
                ** Roll (36 x ??? in.)
                **
                ** approx. 8 min
                */
                pCommInfo->DCBInfo.usWriteTimeout = 48000;
              else
                /*
                ** A (8.5 x 11 in.)
                ** A4 (210 x 297 mm)
                ** B (11 x 17 in.)
                ** A3 (297 x 420 mm)
                **
                ** approx. 60 sec
                */
                pCommInfo->DCBInfo.usWriteTimeout = 6000;
        }
        else
        {
          if (fsMediaSize&(MEDSZ_C|MEDSZ_A2))
            /*
            ** C (17 x 22 in.)
            ** A2 (420 x 594 mm)
            **
            ** approx.  4.5 min
            */
            pCommInfo->DCBInfo.usWriteTimeout = 27000;
          else
            if (fsMediaSize&(MEDSZ_D|MEDSZ_A1|MEDSZ_R24))
              /*
              ** D (22 x 34 in.)
              ** A1 (594 x 841 mm)
              ** Roll (24 x ??? in.)
              **
              ** approx.  6 minutes
              */
              pCommInfo->DCBInfo.usWriteTimeout = 36000;
            else
              if (fsMediaSize&(MEDSZ_E|MEDSZ_A0|MEDSZ_R36))
                /*
                ** E (34 x 44 in.)
                ** A0 (841 x 1189 mm)
                ** Roll (36 x ??? in.)
                **
                ** approx,  10.667 min
                */
                pCommInfo->DCBInfo.usWriteTimeout = 64000;
              else
                /*
                ** A (8.5 x 11 in.)
                ** A4 (210 x 297 mm)
                ** B (11 x 17 in.)
                ** A3 (297 x 420 mm)
                **
                ** approx  3 min
                */
                pCommInfo->DCBInfo.usWriteTimeout = 18000;
        }
        bResult = TRUE;
      }
    }
  }

  if (pIniStr)
    if (GplMemoryFree (pIniStr))
      pIniStr = NULL;

  return  bResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = initialize_comm_port
 *
 *
 * DESCRIPTION   = Set the comm port to the conditions determined from the os2.ini file.
 *
 *
 *
 * INPUT         = pPDevice
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = TRUE  - Port set accordingly
 *
 * RETURN-ERROR  = FALSE - Port could not be set
 *
 *
 *
 **************************************************************************/

LOCAL BOOL  initialize_comm_port(PPDEVICE pPDevice)
{
  BOOL bResult = FALSE;
  PSZ  pszPortName = pPDevice->pszLogAddress;
  COMMINFO CommInfo;

  /*
  ** Look to see if this is a serial port. Basically look at the
  ** name of the file to determined what it is - not very good!
  */
  if (IsIta_COM_Port_Name(pszPortName) && get_ini_comm_info(pPDevice,
                                                          &CommInfo))
  {
    /*
    ** Got the os2.ini information, so on with the show. Need to
    ** set the speed, parity, stop bits and handshake mode. We
    ** also  wish to select a transmit timeout, so that we can
    ** catch dead  devices - not guaranteed, but we try.
    ** The following constants are from the book, so
    ** if you want to know
    ** what is happening, consult Vol 3 of the OS/2 Programmer's
    ** Reference
    */
    if (PrtDevIOCtl(0L, &CommInfo.usBaudRate, 0x41, 1,
                    pPDevice->hDev))
    {
      return  FALSE;
    }

    if (PrtDevIOCtl(0L, &CommInfo.lcData,0x42,1,pPDevice->hDev))
      return  FALSE;

    if (PrtDevIOCtl(0L, &CommInfo.DCBInfo,0x53,1,pPDevice->hDev))
      return  FALSE;

    bResult = TRUE;
  }

  return  bResult;
}

/***************************************************************************
 *
 * FUNCTION NAME = open_file_or_device
 *
 *
 * DESCRIPTION   = Open the file name passed to us.  This
 *                 is where the output will go, and may be
 *                 a com port, an ieee port or a disk
 *                 file.
 *
 *
 *
 * INPUT         = PDDC
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = TRUE  - Open was successful
 *
 *
 * RETURN-ERROR  = FALSE - Open failed,  error logged
 *
 *
 *
 **************************************************************************/

BOOL  open_file_or_device(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  BOOL  bResult = FALSE;
  ULONG ulTemp;
  CHAR  szDeviceDescription[65];
  CHAR  szMessage[90];

#ifdef   DEBUGLOG
  USHORT usAction;                     /* Useless DosOpen parameter         */
#endif

  pPDevice->bRealPlotter = TRUE;       /* assume it is a device             */

#if      IEEESUPPORT
  if (lstrcmp(pPDevice->pszLogAddress, "IEEE") == 0)
  {
    if (pPDevice->IEEEdevice == TRUE)
      Result = TRUE;
    else
      if ((IeeeOpen(&pPDevice->hIEEE,
                    get_ieee_addr(pPDevice->pszLogAddress),
                    0)) == IE_OK)
      {
        pPDevice->IEEEdevice = TRUE;
        Result = TRUE;
      }
      else
        GplErrSetError(PMERR_DOSOPEN_FAILURE);
  }
  else
#endif
    if (!pPDevice->hDev)
    {
      /*
      ** I don't know what all the numbers mean....
      ** psAction(&ulTemp) param will now (os/2 v3) contain
      ** the DosQueryHType info.
      ** pType (PULONG) - output
      **    Address of the value indicating the handle type.
      **
      **    Possible values are shown in the following list:
      **
      **      Bit       Description
      **
      **      15        Network bit:
      **
      **           0   The handle refers to a local file, device, or pipe.
      **           1   The handle refers to a remote file, device, or
      **               pipe.
      **
      **      14        1     Protected file handle.
      **
      **      13-8      Reserved.
      **
      **      7-0       HandleClass: Describes the handle class. It may
      **                take on the following values in the low byte of
      **                pType:
      **
      **           0   Disk file
      **           1   Character device
      **           2   Pipe.
      **
      **                Values greater than 2 are reserved.
      **
      */
      if (PrtOpen(pPDevice->pszLogAddress, (PHFILE)&pPDevice->hDev,
                  &ulTemp, 0L, 0L, (ULONG)0x12, (ULONG)0x42, 0L) == 0)
      {
        /*
        ** if os/2 version 3.0
        */
        if (pDDC->lEngineVersion >= RASTER_ENGINE_22)
        {
          /*
          ** if it is a file turn off prompting for paper
          */
          if ((ulTemp & 0xFF) == 0)
          {
            pPDevice->bRealPlotter = FALSE;       /* it's file            */
          }
        }
        /*
        ** Got it open, so presume it is a comm port &
        ** initialise it
        */
        initialize_comm_port(pPDevice);
                                        // Create t2-thread stuff
        WinLoadString(pPDevice->hAB, hModule, pPDevice->StringID,
                  64, szDeviceDescription);
        WinLoadString((HAB)NULL, hModule, WRITE_ERR,
                       80, szMessage);
        GplThreadStart(pPDevice->hThread,
                       pPDevice->hSpooler,
                       pPDevice->hDev,
                       0L, 0,          // buffer size and count-take defaults
                       '0', 0x8000,    // abort char and abort char count
                       0L, (PBYTE)NULL,// abort buffer size and abort buffer
                       vpszAbortString,
                       pPDevice->pszLogAddress,
                       szMessage,
                       szDeviceDescription,
                       pPDevice->lDcType);
        bResult = TRUE;
      }
      else
        GplErrSetError(PMERR_DOSOPEN_FAILURE);
    }
    else
      bResult = TRUE;

#ifdef   DEBUGLOG
  if (iDebugGo && hfDebug == 0)
    DosOpen("c:\\os2\\dll\\plotters\\plot.log", /* FileName                 */
            &hfDebug,                  /* FileHandle received back          */
            &usAction,                 /* What action DosOpen took          */
            0L,                        /* File size on open                 */
            FILE_NORMAL,               /* attribute = read/write            */
            FILE_CREATE|FILE_TRUNCATE, /* Open flags                        */
            OPEN_ACCESS_WRITEONLY |    /* We can only write to it           */
               OPEN_SHARE_DENYNONE,    /* others can read from it           */
            0L);                       /* Reserved = 0                      */
#endif

  return  bResult;
}

/***************************************************************************
 *
 * FUNCTION NAME =  open_spooler_queue
 *
 *
 * DESCRIPTION   =  Output is destined for the spooler,
 *                  so set up the connection.
 *
 *
 *
 * INPUT         = pDDC
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = File handle to spooler - used for output.
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/

LHANDLE  open_spooler_queue(PDDC pDDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  PSZ QmOpenData[NETWORK_PARAMS+1];
  CHAR  szDeviceDescription[65];
  CHAR  szMessage[90];
  LHANDLE hSpl;

  QmOpenData[ADDRESS] = pPDevice->pszLogAddress;
  QmOpenData[DRIVER_NAME] = pPDevice->pszDriverName;
  QmOpenData[DRIVER_DATA] = (PSZ)pPDevice->pDriverData;
  QmOpenData[DATA_TYPE] = pPDevice->pszDataType ? pPDevice->pszDataType:
                                                  (PSZ)"PM_Q_STD";
  QmOpenData[COMMENT] = pPDevice->pszComment;
  QmOpenData[PROC_NAME] = pPDevice->pszQueueProcName;
  QmOpenData[PROC_PARAMS] = pPDevice->pszQueueProcParams;
  QmOpenData[SPL_PARAMS] = pPDevice->pszSpoolerParams;
  QmOpenData[NETWORK_PARAMS] = pPDevice->pszNetworkParams;

  if (hSpl = SplQmOpen("*", NETWORK_PARAMS+1L, (PQMOPENDATA)QmOpenData))
  {
    // Create t2-thread stuff
    WinLoadString(pPDevice->hAB, hModule, pPDevice->StringID,
                  64, szDeviceDescription);
    WinLoadString((HAB)NULL, hModule, WRITE_ERR,
                  80, szMessage);
    GplThreadStart(pPDevice->hThread,
                   hSpl,
                   pPDevice->hDev,
                   0L, 0,          // buffer size and count-take defaults
                   '0', 0x8000,    // abort char and abort char count
                   0L, (PBYTE)NULL,// abort buffer size and abort buffer
                   vpszAbortString,
                   pPDevice->pszLogAddress,
                   szMessage,
                   szDeviceDescription,
                   pPDevice->lDcType);
  }

  return (hSpl);
}


/***************************************************************************
** FUNCTION : bin_output_bytes
**
** PDDC       pDDC
** PBYTE      pBytes : Pointer to the string of bytes.
** ULONG      nBytes : number of bytes to output
**
** DESCRIPTION   = Outputs binary data by calling the thread code to
**                 output the data on the second thread.
** Note : This function should not be used to output the bytes, before you
**        use output_bytes function atleast once. we are not handling the
**        send_header in this function.
** Simply Outputs nBytes from pBytes to the OutputBuffer
**
** Created : Kran
**
**************************************************************************/
LOCAL VOID  bin_output_bytes(PDDC pDDC,PBYTE pBytes,ULONG ulBytes)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  /*
  ** Only work if direct (to file or com port) or queued and raw
  ** (also * to a file).
  */
  if (pPDevice->bEnableOutput &&
      (pDDC->usDCType == OD_DIRECT ||
       (pDDC->usDCType == OD_QUEUED && pPDevice->DataType == PM_Q_RAW)))
  {

    if (!pPDevice->bPageStarted)
    {
      pPDevice->bSomeRawData = FALSE;
      start_page(pDDC);                /* Initialize everything!            */
    }

    GplThreadOutput(pPDevice->hThread, pBytes, ulBytes, THREAD_DT_BINARY);
  }
  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = output_bytes
 *
 *
 * DESCRIPTION   = write data to the output.
 *
 *                 Bottom level call to write data to
 *                 the output.  May or may not be
 *                 buffered, depending upon
 *                 circumstances.
 *
 *
 *
 * INPUT         = pDDC,pBytes
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = TRUE  - no problems appeared
 *
 *                 (may not be correct answer though)
 *
 * RETURN-ERROR  = FALSE - write failed
 *
 * NOTE : If you change the logic in this function, you should do the same for
 *        bin_output_bytes as well.
 *
 **************************************************************************/

VOID  output_bytes(PDDC pDDC,PSZ pBytes)
{
  PPDEVICE pPDevice = pDDC->pPDevice;

  /*
  ** Only work if direct (to file or com port) or queued and raw
  ** (also * to a file).
  */
  if (pPDevice->bEnableOutput &&
      (pDDC->usDCType == OD_DIRECT ||
       (pDDC->usDCType == OD_QUEUED && pPDevice->DataType == PM_Q_RAW)))
  {

    if (!pPDevice->bPageStarted)
    {
      pPDevice->bSomeRawData = FALSE;
      start_page(pDDC);                /* Initialize everything!            */
    }

    /*
    ** Put data in the current output buffer
    */
    GplThreadOutput(pPDevice->hThread, pBytes,
                   (ULONG)lstrlen((PSZ)pBytes), THREAD_DT_PRINTERLANGUAGE );
  }

  return ;
}

/***************************************************************************
 *
 * FUNCTION NAME = perform_std_spooling
 *
 *
 * DESCRIPTION   = writedata to the spooler.
 *                 End of spooling activities -
 *                 write the metafile data to the spooler.
 *
 *
 *
 * INPUT         = pDDC,hDC
 *
 *
 *
 * OUTPUT        = NONE
 *
 *
 *
 *
 * RETURN-NORMAL = The job ID,  as returned from the spooler
 *
 *
 *
 * RETURN-ERROR  = NONE
 *
 *
 *
 **************************************************************************/
#define SPL_STD_BUFF_SIZE  128
USHORT  perform_std_spooling(PDDC pDDC,HDC hDC)
{
  PPDEVICE pPDevice = pDDC->pPDevice;
  USHORT usJobID = 0;
  HSTD   hJob;
  LONG   lBytesLeft = 0;
  LONG   lBytesToRead = 0;
  LONG   lBytesRead = 0;
  USHORT usBufferSize;
  PBYTE  pBuffer;
  PBYTE  pvTempBuffer;
  USHORT rc;
  BYTE   byteBuff[SPL_STD_BUFF_SIZE];  /* only use if out of mem  */


   /*
   ** Stop present spooling activities, then simply copy the data
   ** into * the spooler handle we accumulated earlier.
   */

  hJob = SplStdStop(hDC);
  pPDevice->bSpoolStdStarted = FALSE;

  /*
  ** If there is any data, copy it to the spooler
  */

  if (lBytesLeft = SplStdQueryLength(hJob))
  {
    /*
    ** Watch out for job lengths greater than 64k-1.
    ** Just do it in 64k-1 chunks.
    */
    if (lBytesLeft > 0xFFFF)
    {
      usBufferSize = 0xFFFF;
    }
    else
    {
      usBufferSize = (USHORT)lBytesLeft;
    }                                  /* endif-else                        */

    /*
    ** Alloc the buffer
    ** If memory alloc fails just use the small output buffer
    */
    rc = DosAllocMem((PVOID*)&pvTempBuffer, usBufferSize,
                      PAG_READ|PAG_WRITE|PAG_COMMIT);

    if (!rc)
    {
      pBuffer = pvTempBuffer;
    }
    else
    {

      /*
      ** handle the error
      ** point to existing buffer and set correct size
      */
      pBuffer = byteBuff;
      usBufferSize = SPL_STD_BUFF_SIZE;
    }

    /*
    ** Get into the correct mode
    */
    start_doc(pDDC);

    /*
    ** Loop getting the metafile data from the GPI and writing
    ** it to the spool file.
    */
    while (lBytesLeft)
    {
      lBytesToRead = lBytesLeft;

      if (lBytesToRead > usBufferSize)
        lBytesToRead = usBufferSize;

      SplStdGetBits(hJob, lBytesRead, lBytesToRead, (PCH)pBuffer);

      /*
      ** Adjust the counts and offsets
      */

      lBytesLeft -= lBytesToRead;
      lBytesRead += lBytesToRead;

      /*
      ** Write it to the spooler for its own interest
      */
      SplQmWrite(pPDevice->hSpooler, lBytesToRead, (PCH)pBuffer);
    }                                  /* end while                         */
    usJobID = end_doc(pDDC);
  }                                    /* endif (job has a length)          */
  SplStdDelete(hJob);                  /* Delete our record of it           */
  pPDevice->bSpoolStdOpened = FALSE;
  SplStdClose(hDC);                    /* Close the SpoolStdOpen            */

  /*
  ** Free segment if it was allocated
  */
  if (pvTempBuffer)
  {
    DosFreeMem(pvTempBuffer);
  }

  return  usJobID;
}


/* end file DOSIO.c */

