/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT (C) Lexmark 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 = PRDQEDOC
 *
 * DESCRIPTIVE NAME =
 *
 *
 * VERSION
 *
 * DATE
 *
 * DESCRIPTION
 *
 * FUNCTIONS   prdq_StartDoc
 *             prdq_EndDoc
 *             prdq_AbortDoc
 *             prdq_AbortTidyUp
 *             prdn_write_abort_tidy_up
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define INCL_32                     /* CON3201 */
#define INCL_DOSPROCESS             /* CON3201 */
#define INCL_DOSSEMAPHORES
#define INCL_GPIPRIMITIVES
#define INCL_GPIERRORS
#define INCL_WINHEAP
#define INCL_GPIBITMAPS
#define INCL_SPL
#define INCL_DOSPROCESS
#define INCL_DEV
#define INCL_DOSMODULEMGR
#define INCL_DOSERRORS
#include <os2.h>
#undef INCL_DOSPROCESS              /* CON3201 */
#undef INCL_DOSSEMAPHORES
#undef INCL_GPIPRIMITIVES
#undef INCL_GPIERRORS
#undef INCL_WINHEAP
#undef INCL_GPIBITMAPS
#undef INCL_SPL
#undef INCL_DOSPROCESS
#undef INCL_DEV
#undef INCL_DOSMODULEMGR
#undef INCL_DOSERRORS

#define INCL_DDIBUNDLES
#define INCL_DDIFONTSTRUCS
#define INCL_DDIDEFS
#include <pmddi.h>
#undef INCL_DDIBUNDLES
#undef INCL_DDIFONTSTRUCS
#undef INCL_DDIDEFS

#define INCL_WINP_SELECTIVE
#define INCL_WINP_SEI
#define INCL_WINP_MISC
#include <pmwinx.h>
#undef INCL_WINP_SELECTIVE
#undef INCL_WINP_SEI
#undef INCL_WINP_MISC
#undef INCL_32                      /* CON3201 */

#include <prdconse.h>
#include <prddcone.h>
#include <prdpcone.h>

#define NO_SYS
#define PRDMTYPE_INCL
#define NO_CONSTANT_INCL
#include <prdinclt.h>
#undef NO_CONSTANT_INCL
#undef PRDMTYPE_INCL
#undef NO_SYS

#include <prdqextf.h>
#include <prdeextf.h>
#include <prdjextf.h>
#include <prdaextf.h>
#include <prdpextf.h>
#include <prdyextf.h>
#include <prdnextf.h>
#include <prdncone.h>

extern ULONG    prdd_Zeroes[];
extern CHAR     szDefaultString[];


/******************************************************************************/
/*                                                                            */
/*   FUNCTION: prdq_StartDoc                                                  */
/*                                                                            */
/*   PARAMETERS:                                                              */
/*                                                                            */
/*   hanDC           DcH;          Handle to DC Instance data                 */
/*   ULONG           ArgInCount;   Number of characters in document           */
/*                                 name                                       */
/*   ULONG far   *   ArgInData;    Position of document name                  */
/*   ULONG far   *   ArgOutCount;  Unused parameter                           */
/*   ULONG far   *   ArgOutData;   Unused parameter                           */
/*   lpDCI           DCIData;      Pointer to DC Instance data                */
/*                                                                            */
/*   DESCRIPTION:                                                             */
/* See prdecopn.c for explanation of states.                                  */
/*                                                                            */
/* State -> EXPLICIT                                                          */
/*                                                                            */
/* StartDoc calls PrepareNewDocument to setup a page if we are in             */
/* WAIT_START or PURGE states.                                                */
/* If in an IMPLICIT state, then drawing may have taken place into            */
/* the document ( which was prepared in Complete_Open ) so we call            */
/* ProcessPage to output this to printer/spooler, and then prepare            */
/* the new document. If no drawing has occured, then the doc is               */
/* ready (from c_open), so we just issue SplQmStartDoc.                       */
/*                                                                            */
/******************************************************************************/
/* CON3201
USHORT pascal prdq_StartDoc ( DcH,
                            ArgInCount,
                            ArgInData,
                            ArgOutCount,
                            ArgOutData,
                            DCIData )

hanDC           DcH;
ULONG           ArgInCount;
PULONG          ArgInData;
PULONG          ArgOutCount;
PUSHORT         ArgOutData;
lpDCI           DCIData;
                         */

SHORT prdq_StartDoc (hanDC           DcH,
                     ULONG           ArgInCount,
                     PULONG          ArgInData,
                     PULONG          ArgOutCount,
                     PUSHORT         ArgOutData,
                     lpDCI           DCIData)

{
#define TFUNC "prdq_StartDoc"

    SHORT       return_value;      /* Changed from USHORT to SHORT since the
                                      value can be negative CON3201          */
    lpPDBI      PDBInstance;       /* Pointer to PDB Instance data           */

    PDBInstance = DCIData->DCIPdbInstance;

#ifdef DEBUG_FSM
    OutputPair(" FSM State is : ", (ULONG)DCIData->State, HEX );
    OutputPrompt("when entering DevEsc StartDoc");
#endif

    switch ( DCIData->State )
    {
        case EXPLICIT:
        case RAW_EXPLICIT:
            /******************************************************************/
            /* Already had a startdoc, so return error.  But may have had an  */
            /* AbortDoc which has not been processed, for a Direct DC.  This  */
            /* will only happen if no drawing has happened (eg                */
            /* StartDoc,AbortDoc,StartDoc ...).  Note if its queued, may be in*/
            /* WaitForThread (unlikely).                                      */
            /******************************************************************/
            if ( (DCIData->Flags & ABORTED) &&
                 (DCIData->Flags & DIRECT)      )
            {
                /**************************************************************/
                /* Write zeroes, SIC, then tidy up.                           */
                /**************************************************************/
                prdn_write_abort_tidy_up( DCIData );

                DCIData->State = WAIT_START;
                /**************************************************************/
                /* CONTINUE!  Prepare for new document etc.                   */
                /**************************************************************/

            }
            else
            {
                return( ERROR_NEG );
            }

            /* Continue ...! */

        case WAIT_START:

            /******************************************************************/
            /* Prepare DC for new document.                                   */
            /******************************************************************/

            prdn_SetStartDocDCIDefaults( DCIData );
            /******************************************************************/
            /* Note: this resets ABORTED flag, so if an AbortDoc has happened,*/
            /* it has no effect.                                              */
            /******************************************************************/

            if ( prdn_PrepareNewDocument( DCIData ) != OK )
            {
                return( ERROR_NEG );
            }
            break;


        case IMPLICIT:
        case RAW_IMPLICIT:
            if ( !(DCIData->Flags & QUEUED_STD) )
            {
                return_value = prdn_ProcessLastPage( DCIData );
                /**************************************************************/
                /* handles Drawn flag etc.  Note that for a one-page implicit */
                /* document, name will be that passed in to this StartDoc     */
                /* call, not Default, as it usually is.                       */
                /**************************************************************/

                if ( return_value != OK )
                {
                    /**********************************************************/
                    /* May have ERROR, ERROR_ABORT, ERROR_PURGE               */
                    /**********************************************************/
                    if ( return_value == ERROR )
                    {
                        /******************************************************/
                        /* Could be memory or setup error.                    */
                        /******************************************************/
                        return( ERROR_NEG );
                    }
                    else
                    {
                        /******************************************************/
                        /* Just continue: we want the new doc to be setup as  */
                        /* usual.                                             */
                        /******************************************************/
                    }

                } /* if !OK */

            } /* if !Q_STD */

            /******************************************************************/
            /* Now prepare for explicit document                              */
            /******************************************************************/
            prdn_SetStartDocDCIDefaults( DCIData );

            if ( prdn_PrepareNewDocument( DCIData ) != OK )
            {
                return( ERROR_NEG );
            }

            break;


        case PURGE:
            /******************************************************************/
            /* old doc already killed - prepare for new one                   */
            /******************************************************************/

            prdn_SetStartDocDCIDefaults( DCIData );

            if ( prdn_PrepareNewDocument( DCIData ) != OK )
            {
                return( ERROR_NEG );
            }
            /* leave purge state */
            break;

        default: /* Error !!! */
            return( ERROR_NEG );


    } /* switch( State ) */

    /**************************************************************************/
    /* Setup name for the new document here.                                  */
    /**************************************************************************/
    if (ArgInData == FNULL || ArgInCount == 0)
    {
        /**********************************************************************/
        /* No name supplied: use default name.                                */
        /**********************************************************************/
        DCIData->DCIDocName = (PULONG)szDefaultString;
    }
    else
    {
        /**********************************************************************/
        /* Use supplied name.                                                 */
        /**********************************************************************/
        DCIData->DCIDocName = ArgInData;
    }

    /**************************************************************************/
    /* Start the spooler document: this causes the job spooling msg to appear */
    /* on 1.3 spooler.                                                        */
    /**************************************************************************/
    if ( DCIData->Flags & QUEUED )
    {
        if ( !(DCIData->Flags & QM_OPEN_ISSUED) )
        {
            /******************************************************************/
            /* Make SplQmOpen call.  PD00227                                  */
            /******************************************************************/
            if (prde_OpenSpooler( PDBInstance) != OK)
            {
                return ( ERROR_NEG );
            }
            DCIData->Flags |= QM_OPEN_ISSUED;
            DCIData->DCISplHandle = PDBInstance->PDBSplHandle;
        }

        if ( SplQmStartDoc(
                  (HSPL)DCIData->DCISplHandle,
                  (PSZ)DCIData->DCIDocName ) != OK )
        {
            return ( ERROR_NEG );
        }
        DCIData->Flags |= QM_START_ISSUED;
    }

    /**************************************************************************/
    /* Now go to explicit state - a document is in progress, and should be    */
    /* terminated by an EndDoc.                                               */
    /**************************************************************************/
    DCIData->State = EXPLICIT;

    return( OK );

}
#undef TFUNC



/******************************************************************************/
/*                                                                            */
/*   FUNCTION: prdq_EndDoc                                                    */
/*                                                                            */
/*   PARAMETERS:                                                              */
/*                                                                            */
/*   hanDC           DcH;           Handle to device context                  */
/*   ULONG           ArgInCount;    Unused parameter                          */
/*   ULONG far   *   ArgInData;     Unused parameter                          */
/*   ULONG far   *   ArgOutCount;   size of ArgOutData info.                  */
/*   ULONG far   *   ArgOutData;    Returns JobId (if Q_RAW or Q_STD)         */
/*   lpDCI           DCIData;       Pointer to DC Instance data               */
/*                                                                            */
/*   DESCRIPTION:                                                             */
/*                                                                            */
/*   This function ends a document in the current device context (if          */
/*   there is one in existence).                                              */
/*                                                                            */
/* State -> WAIT_START                                                        */
/*                                                                            */
/* EndDoc calls ProcessPage to output the page to printer/spooler.            */
/* If EndDoc issued in IMPLICIT state, then there may be an implicit          */
/* document. If drawing has taken place, then the page is output.             */
/*                                                                            */
/* If no drawing has occurred, then the metafiler is stopped and the          */
/* band freed. Also a SplQmEndDoc may need to be issued:                      */
/* a. C_OPEN, no drawing, ENDDOC => SplQmStartDoc has not been issued.        */
/* b. C_OPEN, NEWFRAME, ENDDOC   => SplQmStartDoc has been issued.            */
/* Thus the QM_START_ISSUED flag is used to decide whether to issue           */
/* the SplQmEndDoc.                                                           */
/*                                                                            */
/******************************************************************************/
/*
USHORT pascal prdq_EndDoc ( DcH,
                          ArgInCount,
                          ArgInData,
                          ArgOutCount,
                          ArgOutData,
                          DCIData )

hanDC           DcH;
ULONG           ArgInCount;
PULONG          ArgInData;
PULONG          ArgOutCount;
PUSHORT         ArgOutData;
lpDCI           DCIData;
                          */

SHORT prdq_EndDoc (hanDC           DcH,
                   ULONG           ArgInCount,
                   PULONG          ArgInData,
                   PULONG          ArgOutCount,
                   PUSHORT         ArgOutData,
                   lpDCI           DCIData)

{
#define TFUNC "prdq_EndDoc"


    /**************************************************************************/
    /* Local Variables                                                        */
    /**************************************************************************/
    SHORT      return_value;  /* Changed from USHORT to SHORT since return
                                 value may be negative                      */

#ifdef DEBUG_FSM
    OutputString("DevEsc EndDoc");
#endif

    switch ( DCIData->State )
    {
        case WAIT_START:
            /******************************************************************/
            /* Expecting a StartDoc or BeginClose                             */
            /******************************************************************/
            return( ERROR_NEG );

        case IMPLICIT:
            if (DCIData->Flags & QUEUED_STD)
            {
                /**************************************************************/
                /* StartDoc enforced                                          */
                /**************************************************************/
                return( ERROR_NEG );
            }
            /* continue ... */

        case EXPLICIT:
        case RAW_IMPLICIT:
        case RAW_EXPLICIT:

            if ( (return_value = prdn_ProcessLastPage( DCIData )) != OK )
            {
                /**************************************************************/
                /* May have ERROR, ERR_ABORT, ERR_PURGE                       */
                /**************************************************************/
                if ( return_value == ERROR )
                {
                    return( ERROR_NEG );
                }
                else
                {
                    /**********************************************************/
                    /* continue to end & return( OK )                         */
                    /**********************************************************/
                }

            }

            /******************************************************************/
            /* Return JobID for all QUEUED jobs PD00598                       */
            /******************************************************************/
            if ( ArgOutData && (DCIData->Flags & QUEUED ) )
            {
                /**************************************************************/
                /* JobId returned from QmEndDoc.                              */
                /**************************************************************/
                *ArgOutData  = DCIData->JobId;
//              *ArgOutCount = sizeof(USHORT); /* PD00281...                  */
            }
            break;


        case PURGE:
            /* Already done aborting and tidy up.      */
            /* Now leave PURGE state (goto WAIT_START) */
            break;

    } /* ...switch */

    DCIData->State = WAIT_START;

    return( OK );

}
#undef TFUNC




/******************************************************************************/
/*                                                                            */
/*   FUNCTION: prdq_AbortDoc                                                  */
/*                                                                            */
/*   PARAMETERS:                                                              */
/*                                                                            */
/*   hanDC           DcH;           Handle to device context                  */
/*   ULONG           ArgInCount;    Unused parameter                          */
/*   ULONG far   *   ArgInData;     Unused parameter                          */
/*   ULONG far   *   ArgOutCount;   Unused parameter                          */
/*   ULONG far   *   ArgOutData;    Unused parameter                          */
/*   lpDCI           DCIData;       Pointer to DC Instance data               */
/*                                                                            */
/*   DESCRIPTION:                                                             */
/*                                                                            */
/*   This function aborts a document in the current device context            */
/*   (if one exists).                                                         */
/*                                                                            */
/* State => WAIT_START                                                        */
/*                                                                            */
/* AbortDoc returns OK when PURGEing, or ERROR if in RESET/WAIT_START.        */
/* During a document (explicit or implicit) the abort sets the Aborted        */
/* flag. if DC is direct, then we just return OK: the aborting takes          */
/* place at the next PrtWrite/PrtOpen call.                                   */
/* For a queued DC, we wait for the driver thread to be ready    (ie          */
/* for a drawing/writing operation to finish), and then call                  */
/* abort_tidy_up to perform the abort ( free band, reset Aborted flag,        */
/* etc).                                                                      */
/*                                                                            */
/******************************************************************************/

/* CON3201
USHORT pascal prdq_AbortDoc ( DcH,
                            ArgInCount,
                            ArgInData,
                            ArgOutCount,
                            ArgOutData,
                            DCIData )

hanDC           DcH;
ULONG           ArgInCount;
PULONG          ArgInData;
PULONG          ArgOutCount;
PUSHORT         ArgOutData;
lpDCI           DCIData;
                         */

SHORT prdq_AbortDoc (hanDC           DcH,
                      ULONG           ArgInCount,
                      PULONG          ArgInData,
                      PULONG          ArgOutCount,
                      PUSHORT         ArgOutData,
                      lpDCI           DCIData)

{
#define TFUNC "prdq_AbortDoc"

    /**************************************************************************/
    /* Local Variables                                                        */
    /**************************************************************************/
    TID         tidCurrent;
    USHORT      BytesWritten;  /* number of bytes prtwritten                  */
    USHORT      BytesToDo;     /* number of bytes to prtwrite                 */
    PBYTE       Zeroes;        /* pointer to data to prtwrite                 */
    ULONG       StdHandle;     /* Handle to the metafile                      */
    SHORT       return_value;  /* CON3201 - May be negative change from
                                            USHORT to SHORT                   */


    DCIData->Flags |= ABORTED;

#ifdef DEBUG_FSM
    OutputString("DevEsc AbortDoc");
#endif

    if ( DCIData->Flags & DIRECT )
    {
        /**********************************************************************/
        /* Aborting takes place at next PrtOpen/PrtWrite, and so does the     */
        /* state change to WAIT_START.  Note that we cannot check the State   */
        /* here, as it may be being simultaneously changed elsewhere.  So an  */
        /* abort in WAIT_START (which should really return an error) is not   */
        /* trapped.  However, StartDoc will reset the Aborted flag, so no     */
        /* aborting will take place.  Ditto PURGEing.                         */
        /**********************************************************************/

        return( OK );
    }
    else if ( DCIData->Flags & QUEUED )
    {
        /**********************************************************************/
        /* wait for drawing/QmWrite/etc to finish.                            */
        /**********************************************************************/
        prdn_WaitForThread( DCIData );

        /**********************************************************************/
        /* If we are in WAIT_START, PURGE, or IMPLICIT(1st page) state then   */
        /* ther is no open document to abort, and no band to be freed.        */
        /**********************************************************************/
        if (DCIData->Flags & QM_START_ISSUED)
        {
            /******************************************************************/
            /* tidy up band, closedown.  May return error.                    */
            /******************************************************************/
            return_value = prdn_AbortTidyUp( DCIData );
        }
        else
        {
            /******************************************************************/
            /* Just reset aborted flag and return OK                          */
            /******************************************************************/
            DCIData->Flags &= ~ABORTED;
            return_value = OK;
        }

        /**********************************************************************/
        /* OK to change state, now that we've got the thread.                 */
        /**********************************************************************/
        DCIData->State = WAIT_START;

        prdm_LeaveDriver( DCIData );
        return( return_value );
    }
    /* Note: INFO or MEMORY DCs should not call AbortDoc! */

    return( OK );

}
#undef TFUNC


void prdn_WaitForThread(lpDCI DCIData )
/*
lpDCI   DCIData;   CON3201
*/

{
    TID         tidCurrent;
    PTIB        ptibTemp;
    PPIB        ppibTemp;
    /**************************************************************************/
    /* Note this is basically an EnterDriver call.                            */
    /**************************************************************************/
/*  tidCurrent = DCIData->LIS->tidCurrent;    ----------------------- CON3201 */
    DosGetInfoBlocks(&ptibTemp, &ppibTemp);
    tidCurrent = ptibTemp->tib_ptib2->tib2_ultid;
TRY_AGAIN:
//    StartCrit();
    if ( DCIData->LockDCInfo.tidLock )
    {
        if (DCIData->LockDCInfo.tidLock != tidCurrent)
        {
//            EndCrit();

#ifdef PRD_DEBUG
            DebugOutput("AbortDoc: HDC_BUSY, trying again \n\r");
#endif /* PRD_DEBUG */

            DosSleep(1L);
            goto TRY_AGAIN;
        }
    }
    else
    {
        DCIData->LockDCInfo.tidLock = tidCurrent;
    }

    DCIData->LockDCInfo.LockCount++;
//    EndCrit();

    return;
}


/******************************************************************************/
/*                                                                            */
/* FUNCTION:    prdn_AbortTidyUp                                              */
/*                                                                            */
/* PARAMETERS                                                                 */
/*                                                                            */
/* lpDCI        DCIData                                                       */
/*                                                                            */
/* DESCRIPTION                                                                */
/*                                                                            */
/* Called from AbortDoc (queued) or at a PrtOpen/Write.                       */
/*                                                                            */
/* AbortTidyUp resets the Aborted flag, closes the device, and frees          */
/* the band.                                                                  */
/******************************************************************************/

USHORT prdn_AbortTidyUp(lpDCI DCIData)
{

    ULONG       StdHandle;     /* Handle to the metafile                      */
    USHORT      DosResult;     /* Return value from DOS                       */
    HMODULE     ModuleHandle;  /* The handle to the module                    */
    HMODULE     AbortModuleHandle;/* Module for SplQmAbortDoc                 */
    PFN         pSplQmAbortDoc;/* address of SplQmAbortDoc                    */
    USHORT      Result;        /* Return for SplQmAbortDoc                    */

    DCIData->Flags &= ~ABORTED;

    /**************************************************************************/
    /* Note: ignore errors in here!                                           */
    /**************************************************************************/
    if ( DCIData->Flags & QUEUED )
    {
        /**********************************************************************/
        /* For 1.2 we need to do a SplQMAbortDoc here.  For 1.1 this function */
        /* does not exist so check before using it.                           */
        /**********************************************************************/
/* CON3201 ********************************************************************/
/* No need to worry about 1.1 or 1.2 so will just do the SplQmAbortDoc without*/
/* checking for version.                                                      */
/*      DosResult = DosLoadModule( FNULL,                             CON3201 */
/*                                0,                                  CON3201 */
/*                                 "PMSPL",                           CON3201 */
/*                                 &AbortModuleHandle);               CON3201 */
/*                                                                    CON3201 */
/*      TRACE4(TFUNC, "Abort LoadMod Res", &DosResult, 1);            CON3201 */
/*                                                                    CON3201 */
/*      if ( DosResult == DOS_OK )                                    CON3201 */
/*      {                                                             CON3201 */
            /******************************************************************/
            /* Now try and get the address of SplQmAbortDoc ...               */
            /******************************************************************/
/*          DosResult = DosGetProcAddr( AbortModuleHandle,            CON3201 */
/*                                      "SPLQMABORTDOC",              CON3201 */
/*                                      &pSplQmAbortDoc );            CON3201 */
/*                                                                    CON3201 */
/*          if (DosResult == DOS_OK)                                  CON3201 */
/*          {                                                         CON3201 */
                /**************************************************************/
                /* Can now make the call and free the module                  */
                /**************************************************************/
/*              Result = pSplQmAbortDoc(                              CON3201 */
/*                         (HSPL)DCIData->DCISplHandle);              CON3201 */
/*              DosResult = DosFreeModule( AbortModuleHandle);        CON3201 */
/*                                                                    CON3201 */
/*              if (Result != OK)                                     CON3201 */
/*              {                                                     CON3201 */
/*                  prdm_LeaveDriver(DCIData);                        CON3201 */
/*                  return((USHORT)ERROR_NEG);                        CON3201 */
/*              }                                                     CON3201 */
/*          }                                                         CON3201 */
/*                                                                    CON3201 */
            /******************************************************************/
            /* Failed to get the address so free the module.                  */
            /******************************************************************/
/*          DosResult = DosFreeModule( AbortModuleHandle);            CON3201 */
/*      }                                                             CON3201 */
        /* This is where we now do the AbortDoc           CON3201 */
        Result = SplQmAbortDoc(                        /* CON3201 */
                   (HSPL)DCIData->DCISplHandle);       /* CON3201 */
        if (Result != OK)                              /* CON3201 */
        {                                              /* CON3201 */
            prdm_LeaveDriver(DCIData);                 /* CON3201 */
            return(ERROR_NEG);                         /* CON3201 */
        }                                              /* CON3201 */

        if (DCIData->Flags & QUEUED_STD)
        {
            if ( !(StdHandle = (ULONG)SplStdStop ((HDC)DCIData->DcH)) )
            {
                return ( ERROR_NEG );
            }


            if ( SplStdDelete ((HSTD)StdHandle) != OK )
            {
                return ( ERROR_NEG );
            }
        }

    } /* ...if queued */
    else if ( (DCIData->Flags & DIRECT) &&
              (DCIData->Flags & PRINTER_OPEN) )
    {
        /**********************************************************************/
        /* If PrtClose fails, then PrtAbort and retry.                        */
        /* Note: OS 1.1 does not have PrtAbort...                             */
        /**********************************************************************/
        while( (Result =PrtClose(DCIData->DCIPdbInstance->PDBHandle)) &&
               (Result != ERROR_INVALID_HANDLE) )
        {
            prdn_our_PrtAbort( DCIData );
        }
    }


    prdj_FreeBand( DCIData, &DCIData->DCICurrBand );

    return( OK );

}


/******************************************************************************/
/*                                                                            */
/* FUNCTION:    prdn_write_abort_tidy_up                                      */
/*                                                                            */
/* PARAMETERS                                                                 */
/*                                                                            */
/* lpDCI        DCIData                                                       */
/*                                                                            */
/* DESCRIPTION                                                                */
/*                                                                            */
/* Called from prdn_PrtWrite if aborting and not purging.                     */
/*                                                                            */
/* Writes some zeros & a SIC to the printer, then calls abort_tidy_up         */
/* to free the band, close the printer, reset the aborted flag.               */
/*                                                                            */
/* NOTE : DO NOT USE PrintSimple or anything which calls prdn_PrtWrite        */
/* within this function!!!                                                    */
/*                                                                            */
/******************************************************************************/
USHORT prdn_write_abort_tidy_up(lpDCI DCIData )

/*CON3201 lpDCI   DCIData;*/

{
/*  USHORT      BytesWritten;     number of bytes prtwritten         CON3201  */
/*  USHORT      BytesToDo;        number of bytes to prtwrite        CON3201  */
    ULONG       BytesWritten;  /* number of bytes prtwritten                  */
    ULONG       BytesToDo;     /* number of bytes to prtwrite                 */
    PBYTE       Zeroes;        /* pointer to data to prtwrite                 */
/*  USHORT      Result;           Return for SplQmAbortDoc           CON3201  */
    ULONG       Result;        /* Return for SplQmAbortDoc                    */
    USHORT      i;
    CHAR        FFBuffer[20];  /* PD00105 : form feed buffer                  */
    USHORT      FFBufferIndex; /* PD00105 :                                   */
    lpPDBI               PDBI; /* PD00733 :                                   */
    lpDDTType            pDDT; /* PD00733 :                                   */
    BYTE                 Byte; /* PD00733 :                                   */

    /**************************************************************************/
    /* PD00733 : initialise local pointers                                    */
    /**************************************************************************/
    PDBI = DCIData->DCIPdbInstance;
    pDDT = &(DCIData->DCIPdbInstance->DDT);

    /**************************************************************************/
    /* Tidy up printer by writing some zeroes to finish off a line of         */
    /* graphics.  Then write a SIC.  If either fails, do a PrtAbort.  Finally,*/
    /* do the normal aborting things.                                         */
    /**************************************************************************/

    /**************************************************************************/
    /* Need to send 64K of zeroes because this is the maximum size of the     */
    /* raster commands.  Note that this driver never generates 64K but that a */
    /* raw data job generated by someone else may.                            */
    /*                                                                        */
    /* If printer is in graphics mode this will ensure it gets all the data it*/
    /* is expecting before sending a SIC.                                     */
    /**************************************************************************/
    Zeroes = (PBYTE)prdd_Zeroes;

    for ( i = 0; i < 64; i++ )      /* This might need to be increased to */
    {                               /* (ULONG)0xFFFFFFFF / 1024 but will  */
        BytesToDo = 1024;           /* leave at 64 for now                */

        Result = PrtWrite( DCIData->DCIPdbInstance->PDBHandle,
                           Zeroes,
                           BytesToDo,
                           (PULONG)&BytesWritten );

        if ( !(!Result && (BytesToDo == BytesWritten)) )
        {
            /******************************************************************/
            /* PrtWrite failed.  Ignore (as this is already aborting) but     */
            /* issue PrtAbort.                                                */
            /******************************************************************/
            prdn_our_PrtAbort( DCIData );
            goto EXIT_ABORT_TIDY_UP;
        }
    }

    /**************************************************************************/
    /* Attempt a form feed: if the printer is dead this won't work so don't   */
    /* check return value.                                                    */
    /**************************************************************************/
    /**************************************************************************/
    /* PD00105 : Don't call prdp_PrintSimple as above.  Call prdp_AddString   */
    /* and PrtWrite to attempt the form feed.                                 */
    /**************************************************************************/

    /**************************************************************************/
    /* PD00733 : Send SIC command for any printer that accepts a SIC.         */
    /* Replaced the PD00105 prdp_AddString form feed call with an end of job  */
    /* call. The end of job will be a form feed when the printer does not     */
    /* support a SIC.                                                         */
    /*                                                                        */
    /* For 4019 driver, always issue the SIC - as this does not cause a feed  */
    /* if no drawing has occurred, and is required to set the printer back to */
    /* the correct state after a job.  Similarly for 3816 printers, and       */
    /* Nile/Tiber printers.                                                   */
    /**************************************************************************/

    Byte = 0;

    FFBufferIndex = 0;

    prdp_AddString (
                    DCT_END_OF_JOB,
                    USE_PRINTER_TABLE,
                    (PBYTE)&Byte,
                    FFBuffer,
                    (PUSHORT)&FFBufferIndex,
                    20,
                    DCIData );

    BytesToDo = FFBufferIndex;

    Result = PrtWrite( DCIData->DCIPdbInstance->PDBHandle,
                       FFBuffer,
                       BytesToDo,
                       (PULONG)&BytesWritten );

    if ( !(!Result && (BytesToDo == BytesWritten)) )
    {
        /**********************************************************************/
        /* PrtWrite failed.  Ignore (as this is already aborting) but issue   */
        /* PrtAbort.                                                          */
        /**********************************************************************/
        prdn_our_PrtAbort( DCIData );
    }
    /**************************************************************************/
    /* PD00105 : End of added code for this ptr.                              */
    /**************************************************************************/

EXIT_ABORT_TIDY_UP:
    /**************************************************************************/
    /* Now do the rest                                                        */
    /**************************************************************************/
    return( prdn_AbortTidyUp( DCIData ) );

}
