HoseIrda.c

/*
    File:       HoseIrda.c
 
    Contains:   Sample code to demonstrate how a LaserWriter communication hose 
                plugin can be written. An Irda (Infrared) hose is used here.
 
    Written by: Chorng-Shyan Lin and Ingrid Kelly   
 
    Copyright:  Copyright © 1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/26/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                5/5/99      lin             changed the way cntl-D is sent at close time,
                                            longer time-out
                
 
*/
 
#include <CommResources.h>
#include <CTBUtilities.h>
#include <Connections.h>
 
#include "Hose.h"
#include "HoseIrda.h"
#include "PSWriterErr.h"                    
 
#define LockHint(collection, tag, id)   SetCollectionItemInfo((collection), (tag), (id), kCollectionLockMask, kCollectionLockMask)      
 
/*  The following two macros are intended to make it easier to
    declare routines that will be invoked by the MixedMode Mgr.
    DeclareProcPtr() will create a routine descriptor with the
    name g_'proc' if we are compiling in an environment that
    needs routine descriptors. If we are in the old 68K environment
    then this macro does nothing. MakeProcPtr creates a reference to
    the function pointer 'proc'. If we are using routine descriptors
    then the reference is the address of the descriptor created by
    DeclareProcPtr(). If we are in a 68K world then the reference
    is just the proc pointer itself.
*/
#if TARGET_RT_MAC_CFM
 
#define DeclareProcPtr(proc, upp)   \
        RoutineDescriptor g_##proc = BUILD_ROUTINE_DESCRIPTOR(upp, proc);
#define ExternalProcPtr(proc)   \
        extern RoutineDescriptor g_##proc;
#define MakeProcPtr(proc, procType) \
        ((procType)&g_##proc)
        
#else
 
#define DeclareProcPtr(proc, upp)
#define ExternalProcPtr(proc)
#define MakeProcPtr(proc, procType) \
        ((procType)proc)
        
#endif
 
/*** Constants ***/
#define kClosingTimeOut 3600
 
/*** Variable Types ***/
struct IrdaHoseRec {
    ConnHandle                          connectionH;
    ConnState                           connectionState;
    MemQElemPtr                         writeMemQElemPtr;
    MemQElemPtr                         readMemQElemPtr;
    BufCallbacks                        callbacks;
    Boolean                             eojFired;
    Boolean                             needToDumpDataOut;
    Boolean                             needToRequestStatus;
    OSErr                               openResult;
    Boolean                             needCloseEoj;   
    long                                startCloseTime; 
};
typedef struct IrdaHoseRec          IrdaHoseRec;
typedef IrdaHoseRec*                IrdaHoseRecPtr;
 
 
/*** Prototypes ***/
OSErr InitLib(CFragInitBlockPtr initBlkPtr);                                                        
void TerminateLib(void);
 
// hose API
static OSStatus closeIrdaHose(void *refcon);
static OSStatus outIrdaHose(void *refcon, MemQElemPtr memElem);
static OSStatus inIrdaHose(void *refcon, MemQElemPtr memElem);
static OSStatus idleIrdaHose(void *refcon);
static OSStatus statusIrdaHose(void *refcon, StringPtr statusStr);              
static OSStatus disposeIrdaHose(void *refcon);                              
static ConnState connStateIrdaHose(void *refcon);
 
// callback from CTB
static  void commOpenCompletion(ConnHandle connectionH);
static  void commWriteCompletion(ConnHandle connectionH);
static  void commReadCompletion(ConnHandle connectionH);
static  void commStatusCompletion(ConnHandle connectionH);
 
static  void closingEojSentCompletion(ConnHandle connectionH);
static  void closingEojReceivedCompletion(ConnHandle connectionH);
 
// utilities
static OSStatus requestStatus(IrdaHoseRecPtr irdaHoseP);
static OSStatus cmWritePending(ConnHandle connectionH, Boolean* writePendingP);
 
static OSErr translateErr(OSErr err);
static void StripEOJs(unsigned char* buffer, SInt32* nBytesP);  // lin016
static OSErr UnLockHint(Collection collection, CollectionTag tag, long id);
 
/***Globals**/
unsigned char   gInCntlD = 0x04;
unsigned char   gOutCntlD = 0x04;
unsigned char   gCntlT = 0x14;
long    gInOne = 1;
long    gOutOne = 1;
char *gIrDALaserconfigStr = "myClass: LaserPrinter myAttr: IrDA:IrLMP:LsapSel peerClass: IrLPT peerAttr: IrDA:IrLMP:LsapSel\0";
 
DeclareProcPtr(commOpenCompletion, uppConnectionCompletionProcInfo)
DeclareProcPtr(commWriteCompletion, uppConnectionCompletionProcInfo)
DeclareProcPtr(commReadCompletion, uppConnectionCompletionProcInfo)
DeclareProcPtr(commStatusCompletion, uppConnectionCompletionProcInfo)
 
DeclareProcPtr(closingEojSentCompletion, uppConnectionCompletionProcInfo)
DeclareProcPtr(closingEojReceivedCompletion, uppConnectionCompletionProcInfo)
 
/*** Code ***/
OSErr InitLib(CFragInitBlockPtr /* initBlkPtr */)
{
    return noErr;
}
 
void TerminateLib(void)
{
    // any clean up if needed
    // do nothing in this case
}                                                       
 
 
#pragma mark -
#pragma export on
OSStatus hoseOpen(HoseInfo *hoseInfo, BufCallbacks *callbacks, Collection hints, Handle /* papaH */)        
/*  
Fill out 'hose' with information that describes the IrDA hose
and Open connection to IrDA printer.
*/
{
OSStatus err = noErr;
short irdaProcID = -1;  // -1 is invalid ProcID
ConnHandle connectionH = 0;
CMBufferSizes irDaBufSizes;
IrdaHoseRecPtr irdaHoseP = 0;
Boolean eighthBit = false;                  
Boolean transparentBit = false;                 
short i;
 
    // fill out the proc pointers to our hose
    hoseInfo->out = outIrdaHose;        
    hoseInfo->in = inIrdaHose;              
    hoseInfo->idle = idleIrdaHose;          
    hoseInfo->close = closeIrdaHose;        
    hoseInfo->connState = connStateIrdaHose;        
    hoseInfo->status = statusIrdaHose;  
    hoseInfo->dispose = disposeIrdaHose;
    // buffer info
    hoseInfo->bufSize = kPrinterHoseBufSize;            
    hoseInfo->minBufs = 4;              
    hoseInfo->maxBufs = kPrinterHoseMaxBufs;                
    // add necessary hints
    err = UnLockHint(hints, kHintEighthBitTag, kHintDataFormatId);
    err = AddCollectionItem(hints, kHintEighthBitTag, kHintDataFormatId, sizeof(eighthBit), &eighthBit);
    if(!err) err = LockHint(hints, kHintEighthBitTag, kHintDataFormatId);               
 
    if(!err) err = UnLockHint(hints, kHintTransparentChannelTag, kHintTransparentChannelId);
    if(!err) err = AddCollectionItem(hints, kHintTransparentChannelTag, kHintTransparentChannelId, 
                                        sizeof(transparentBit), &transparentBit);
    if(!err) err = LockHint(hints, kHintTransparentChannelTag, kHintTransparentChannelId);  
    
    // allocate private storage for the hose
    if(!err) irdaHoseP = (IrdaHoseRecPtr)NewPtrClear(sizeof(IrdaHoseRec));              
    if(irdaHoseP){
        hoseInfo->refcon = (void *)irdaHoseP;               
        irdaHoseP->connectionH = 0;
        irdaHoseP->callbacks = *callbacks;
        if(InitCRM != 0){   // if there is Communication Toolbox, initialize it
            err = InitCRM();
            if(!err)
                err = InitCTBUtilities();
            if(!err)
                err = InitCM();
            if(!err){
                irdaProcID = CMGetProcID("\pIrDA Tool");    // name of IrDA tool
                if(irdaProcID != -1){
                    for(i = cmDataIn; i <= cmRsrvOut ; i++)
                        irDaBufSizes[i] = 0;    // let IrDA tool decide buffer sizes
                    // make a new connection record
                    irdaHoseP->connectionH = connectionH = CMNew(irdaProcID, cmData | cmNoMenus | cmQuiet, irDaBufSizes, (long)irdaHoseP, 0);
                    if(connectionH){
                        // configure the connection record
                        err = CMSetConfig (connectionH, gIrDALaserconfigStr);
                        if(err == cmNoErr){
                            irdaHoseP->connectionState = kConnOpening;  
                            // try to open a connection async.                      
                            err = CMOpen(connectionH, true, MakeProcPtr(commOpenCompletion, ConnectionCompletionUPP), -1);  
                            if(err != cmNoErr)
                                irdaHoseP->connectionState = kConnClosed;
                        }else{
                            err = irdaHoseP->openResult = errIrdaUnknownOpenErr;
                            /* CTB does not provide a good error code */                
                        }
                    }else{
                        err = irdaHoseP->openResult = errIrdaUnknownOpenErr;    
                        /* CTB does not provide a good error code, possibly out of memory */                
                    }
                }else{
                    err = irdaHoseP->openResult = errIrdaToolNotFoundErr;   // no comm. tool for irDA
                }
            }
        }else{
            err = irdaHoseP->openResult = errHoseCantBeUsed;    // CTB not available
        }
    }else{
        err = irdaHoseP->openResult = memFullErr;   // out of memory
    }
    if(err){    // clean up
        if(connectionH)
            CMDispose(connectionH);
        if(irdaHoseP){
            DisposePtr((Ptr)irdaHoseP);
            hoseInfo->refcon = 0;
        }
    }
    return (translateErr(err));
}
#pragma export off
 
 
static OSStatus statusIrdaHose(void *refcon, StringPtr /* statusStr */)
{
IrdaHoseRecPtr irdaHoseP;
OSStatus err = noErr;
 
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        irdaHoseP->needToRequestStatus = true;
        // request status from printer
        err = requestStatus(irdaHoseP);
    }
    if(err){
        err = statusErr;
        if(irdaHoseP && irdaHoseP->openResult)
            err = irdaHoseP->openResult;
    }
    return (err);
}
 
static OSStatus closeIrdaHose(void *refcon)
{
OSStatus err = noErr;
IrdaHoseRecPtr irdaHoseP;
ConnHandle connectionH;
Boolean writePending;
    
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        irdaHoseP->connectionState = kConnClosing;
        if((connectionH = irdaHoseP->connectionH) != 0){
            // depending on the type of hose and printer, we may not want to
            // report "closed" until the printer is done and ready for the next job
            // in this particular hose, we wait for the printer to respond to our extra eoj
            irdaHoseP->startCloseTime = TickCount();
            irdaHoseP->needCloseEoj = true;
            err = cmWritePending(connectionH, &writePending);
            if(!err && !writePending){
                irdaHoseP->needCloseEoj = false;
                gOutOne = 1;
                err = CMWrite(connectionH,
                             &gOutCntlD,
                             &gOutOne,
                             cmData,
                             true,
                             MakeProcPtr(closingEojSentCompletion, ConnectionCompletionUPP),
                             -1,
                             0);
                         
            }
        }
    }
    return (noErr);
}
 
/*
    routine to write data stream out, async.
*/
static OSStatus outIrdaHose(void *refcon, MemQElemPtr memElem)
{
OSStatus err = paramErr;                                        
IrdaHoseRecPtr irdaHoseP;
ConnHandle connectionH;
Boolean writePending;
 
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        if(irdaHoseP->connectionState == kConnClosing){
            err = noErr;    // ignore it if we are already closing
        }else{
            if(irdaHoseP->connectionState == kConnOpen){    // if the hose is open
                irdaHoseP->writeMemQElemPtr = memElem;
                irdaHoseP->eojFired = false;
                if((connectionH = irdaHoseP->connectionH) != 0){
                    // CTB does not allow multiple writes, may have to postpone it
                    err = cmWritePending(connectionH, &writePending);
                    if(!err){
                        irdaHoseP->needToDumpDataOut = writePending ? true : false;
                        if(!writePending){
                            err = CMWrite(connectionH,
                                         memElem->buf,
                                         &(memElem->nBytes),
                                         cmData,
                                         true,
                                         MakeProcPtr(commWriteCompletion, ConnectionCompletionUPP),
                                         -1,
                                         0);
                            if(err){
                                err = errIrdaDisconnectedErr;   
                                err = translateErr(err);                    
                            }
                        }
                    }
                }
            }else{
                err = PAPNoPrinter;
            }
        }
    }
    if(err){
        if(irdaHoseP && irdaHoseP->openResult)
            err = irdaHoseP->openResult;
    }
    return err;                                                     
}
 
 
/*
    routine to read data stream in, async.
*/
static OSStatus inIrdaHose(void *refcon, MemQElemPtr memElem)
{
OSStatus err = paramErr;                                        
IrdaHoseRecPtr irdaHoseP;
ConnHandle connectionH;
long numOfBytes;
CMBufferSizes cmBufSizes;
CMStatFlags cmFlags;
 
    memElem->nBytes = 0;
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        if(irdaHoseP->connectionState == kConnClosing){
            err = noErr;    // ignore it if we are already closing
        }else{
            if(irdaHoseP->connectionState == kConnOpen){    // if the hose is open
                irdaHoseP->readMemQElemPtr = memElem;
                if((connectionH = irdaHoseP->connectionH) != 0){
                    err = CMStatus(connectionH,
                                    cmBufSizes,
                                    &cmFlags);
                    if(!err){
                        numOfBytes = cmBufSizes[cmDataIn];  // number of bytes available
                        if(numOfBytes > memElem->maxBytes)
                            numOfBytes = memElem->maxBytes;
                        if(numOfBytes <= 0)
                            numOfBytes = 1; // try to read at least one byte
                        err = CMRead(connectionH,
                                     memElem->buf,
                                     &numOfBytes,
                                     cmData,
                                     true,
                                     MakeProcPtr(commReadCompletion, ConnectionCompletionUPP),
                                     -1,
                                     0);
                        if(err){
                            err = errIrdaDisconnectedErr;   
                            err = translateErr(err);                    
                        }
                    }
                }
            }else{
                err = PAPNoPrinter;
            }
        }
    }
    if(err){
        if(irdaHoseP && irdaHoseP->openResult)
            err = irdaHoseP->openResult;
    }
    return err;                                                     
}
 
 
static OSStatus idleIrdaHose(void *refcon)
{
IrdaHoseRecPtr irdaHoseP;
ConnHandle connectionH;
OSStatus err = noErr;
 
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        if((connectionH = irdaHoseP->connectionH) != 0){
            CMIdle(connectionH);
            // take this chance to request status if necessary
            requestStatus(irdaHoseP);
        }
        err = irdaHoseP->openResult ? irdaHoseP->openResult : noErr;
    }
    return(err);
}
 
 
static OSStatus disposeIrdaHose(void *refcon)
{
OSStatus err = noErr;
IrdaHoseRecPtr irdaHoseP;
ConnHandle connectionH;
 
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        if((connectionH = irdaHoseP->connectionH) != 0){    
            // close connection
            err = CMClose(connectionH, false, 0, -1, true);
            // dispose connection record
            CMDispose(connectionH);
        }   
        // dispose private storage
        DisposePtr((Ptr)irdaHoseP);
    }
    return noErr;
}
 
 
/*  Return the current state of the hose
*/
static ConnState connStateIrdaHose(void *refcon)
{
IrdaHoseRecPtr irdaHoseP;
ConnHandle connectionH;
ConnState connState = kConnClosed;
OSStatus err = noErr;
Boolean writePending;
 
    if((irdaHoseP = (IrdaHoseRecPtr)refcon) != 0){
        if(irdaHoseP->connectionState == kConnClosing){ // if we are closing down the hose
            if((TickCount() - irdaHoseP->startCloseTime) > kClosingTimeOut){    // test time-out
                irdaHoseP->connectionState = kConnClosed;
            }else{
                // write an extra eoj to printer as part of closing process
                connectionH = irdaHoseP->connectionH;
                if(irdaHoseP->needCloseEoj && connectionH){
                    err = cmWritePending(connectionH, &writePending);
                    if(!err && !writePending){
                        gOutOne = 1;
                        err = CMWrite(connectionH,
                                     &gOutCntlD,
                                     &gOutOne,
                                     cmData,
                                     true,
                                     MakeProcPtr(closingEojSentCompletion, ConnectionCompletionUPP),
                                     -1,
                                     0);
                        irdaHoseP->needCloseEoj = false;
                    }
                }
            }
        }
        connState = irdaHoseP->connectionState;
    }
    return connState;
}
 
 
/*
    This call-back routine is called by CTB when our open request completes
*/
static void commOpenCompletion(ConnHandle connectionH)
{
IrdaHoseRecPtr irdaHoseP;
 
    if(connectionH){
        irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
        if(irdaHoseP){
            irdaHoseP->connectionState = (*connectionH)->errCode ? kConnClosed : kConnOpen;
            irdaHoseP->openResult = (*connectionH)->errCode ? PAPNoPrinter : noErr;
        }
    }
}
 
static  void closingEojSentCompletion(ConnHandle connectionH)
{
IrdaHoseRecPtr irdaHoseP;
OSStatus err;
 
    if(connectionH){
        if((irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon) != 0){
            err = (*connectionH)->errCode;
            if(err == noErr){
                gInOne = 1;
                err = CMRead(connectionH,
                             &gInCntlD,
                             &gInOne,
                             cmData,
                             true,
                             MakeProcPtr(closingEojReceivedCompletion, ConnectionCompletionUPP),
                             -1,
                             0);
            }
        }
    }   
}
 
static  void closingEojReceivedCompletion(ConnHandle connectionH)
{
IrdaHoseRecPtr irdaHoseP;
 
    /* we can further test if the received data contain eoj from the printer. 
       we don't do this here b/c we seem to have posponed the closing report long enough
       already and also it could be an overkill to wait for the eoj.
    */
    if(connectionH){
        if((irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon) != 0){
            irdaHoseP->connectionState = kConnClosed;
        }
    }
}
 
static void commWriteCompletion(ConnHandle connectionH)
{
IrdaHoseRecPtr irdaHoseP;
MemQElemPtr writeMemQElemPtr;
OSStatus err;
 
    if(connectionH){
        irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
        if(irdaHoseP){
            writeMemQElemPtr = irdaHoseP->writeMemQElemPtr;
            err = (*connectionH)->errCode;
            if(err){
                err = errIrdaDisconnectedErr;   
                err = translateErr(err);                    
            }
 
            // if we have sent an eoj as part of the previous write request
            if(irdaHoseP->eojFired){    
                requestStatus(irdaHoseP);   // give status request a chance
                
                irdaHoseP->eojFired = false;
                // notify client of completion
                if(err && (irdaHoseP->connectionState == kConnClosing))
                    err = noErr;
                if(irdaHoseP->callbacks.finishedWrite)
                    (irdaHoseP->callbacks.finishedWrite)(writeMemQElemPtr, err);
            }else{
                if(!err && writeMemQElemPtr->eoj){  // need to send an eoj
                    irdaHoseP->eojFired = true;
                    gOutOne = 1;
                    err = CMWrite(connectionH,
                                 &gOutCntlD,
                                 &gOutOne,
                                 cmData,
                                 true,
                                 MakeProcPtr(commWriteCompletion, ConnectionCompletionUPP),
                                 -1,
                                 0);
                    // notify client of error
                    if(err){
                        irdaHoseP->eojFired = false;
                        if(err && (irdaHoseP->connectionState == kConnClosing))
                            err = noErr;
                        if(irdaHoseP->callbacks.finishedWrite)
                            (irdaHoseP->callbacks.finishedWrite)(writeMemQElemPtr, err);
                    }
                }else{
                    requestStatus(irdaHoseP);   // give status request a chance
                    if(err && (irdaHoseP->connectionState == kConnClosing))
                        err = noErr;
                    // notify client of completion
                    if(irdaHoseP->callbacks.finishedWrite)
                        (irdaHoseP->callbacks.finishedWrite)(writeMemQElemPtr, err);
                }
            }
        }
    }
}
 
static void commReadCompletion(ConnHandle connectionH)
{
IrdaHoseRecPtr irdaHoseP;
MemQElemPtr readMemQElemPtr;
OSStatus err;
 
    if(connectionH){
        err = (*connectionH)->errCode;
        if(err){
            err = errIrdaDisconnectedErr;               
            err = translateErr(err);                    
        }
        irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
        if(irdaHoseP){
            if(err && (irdaHoseP->connectionState == kConnClosing))
                err = noErr;
            readMemQElemPtr = irdaHoseP->readMemQElemPtr;
            if(readMemQElemPtr){
                readMemQElemPtr->nBytes = *(SInt32*)(&((*connectionH)->asyncCount[cmDataIn]));
                StripEOJs(readMemQElemPtr->buf, &readMemQElemPtr->nBytes);  
                if(irdaHoseP->callbacks.finishedRead)
                    (irdaHoseP->callbacks.finishedRead)(readMemQElemPtr, err);
            }
        }
    }
}
 
 
static void commStatusCompletion(ConnHandle connectionH)
{
IrdaHoseRecPtr irdaHoseP;
OSStatus err;
 
    if(connectionH){
        irdaHoseP = (IrdaHoseRecPtr)(*connectionH)->refCon;
        // if there is data waiting to be sent
        if(irdaHoseP && (irdaHoseP->needToDumpDataOut)){
            irdaHoseP->needToDumpDataOut = false;
            err = CMWrite(connectionH,
                         irdaHoseP->writeMemQElemPtr->buf,
                         &(irdaHoseP->writeMemQElemPtr->nBytes),
                         cmData,
                         true,
                         MakeProcPtr(commWriteCompletion, ConnectionCompletionUPP),
                         -1,
                         0);
        }   
    }
}
 
 
static OSStatus requestStatus(IrdaHoseRecPtr irdaHoseP)
{
OSStatus err = noErr;
ConnHandle connectionH;
Boolean writePending;
 
    if((irdaHoseP->connectionState == kConnOpen) && (irdaHoseP->needToRequestStatus)){
        if((connectionH = irdaHoseP->connectionH) != 0){
            err = cmWritePending(connectionH, &writePending);
            
            // CTB does NOT allow multiple writes
            if(!err && !writePending){
                irdaHoseP->needToRequestStatus = false;
                gOutOne = 1;
                // request status from printer
                err = CMWrite(connectionH,
                             &gCntlT,
                             &gOutOne,
                             cmData,
                             true,
                             MakeProcPtr(commStatusCompletion, ConnectionCompletionUPP),
                             -1,
                             0);
            }
        }   
    }
    return (err);
}
 
static OSStatus cmWritePending(ConnHandle connectionH, Boolean* writePendingP)
{
OSStatus err;
CMBufferSizes cmBufSizes;
CMStatFlags cmFlags;
 
    *writePendingP = false;
    err = CMStatus(connectionH,
                    cmBufSizes,
                    &cmFlags);
    // CTB does NOT allow multiple writes
    if(!err && (cmFlags & cmStatusDWPend))
        *writePendingP = true;
    return (err);
}
 
 
/* 
translate connection Mgr. errors to something meaningful
*/
static OSErr translateErr(OSErr err)
{
OSErr newErr = err;
 
    switch(err){
        case cmNotOpen:
        case readErr:                   // readErr and writErr should be mapped to 'errIrdaDisconnectedErr' when
        case writErr:                   // we can supply better error reporting for that error. 
        case cmNotClosed:               // This should be 'errHoseInUse' when we can supply better error reporting. 
        case errIrdaDisconnectedErr:    // also make sure that this error is mapped  
            newErr = PAPNoPrinter;
        break;
        
        case errIrdaUnknownOpenErr:                      
            newErr = errUnableToDoNewIrdaConnection;
        break;
        
        case cmNoTools:
            newErr = errIrdaToolNotFoundErr;
        break;
        
        case cmGenericError:
        case cmRejected:
        case cmFailed:
        case cmTimeOut:
        case cmNoRequestPending:
        case cmNotSupported:
        case cmUserCancel:
        case cmUnknownError:
            newErr = errGenericComponentErr;
        break;
    }
    return newErr;
}
 
 
 
static void StripEOJs(unsigned char* buffer, SInt32* nBytesP)       
{
    unsigned char *src, *dst;
    SInt32  count;
 
    src = dst = buffer;
    count = *nBytesP + 1;   // +1 for pre-decrement
 
    while (--count)
    {
        if ((*dst++ = *src++) == '\x04')
        {
            dst--;          // Opps, we hit a control-D, backup a character & decrement num bytes
            (*nBytesP)--;
        }
    }
}
 
 
static OSErr UnLockHint(Collection collection, CollectionTag tag, long id)
/*  If the hint is locked UnLockHint() will unlock it, if
    it is unlocked UnLockHint() will do nothing.
*/
{
    OSErr err = noErr;
    long attributes;
     
    err = GetCollectionItemInfo(collection, tag, id, kCollectionDontWantIndex, kCollectionDontWantSize, &attributes);
    if(!err){
        attributes = attributes & ~kCollectionLockMask;                                 
        err = SetCollectionItemInfo(collection, tag, id, kCollectionLockMask, attributes);
    }
    if(err == collectionItemNotFoundErr) err = noErr;       //If it wasn't there that's fine.
    
    return err;
}