Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
PAPPostScriptStuff.c
/* |
File: PAPPostScriptStuff.c |
Contains: The following are routines used to support the handling of Postscript query |
processing. These are my own routines implemented to provide support for |
the postscript queries from a laserwriter client. All of the postscript |
parsing related functions are included in this file. I'd like to warn the |
reader that this code is written to handle some basic postscript queries as |
sent by Apple LaserWriter Client prior to v8.5. |
The design of the routine presented here are to |
1. recognize postscript queries as opposed to postscript data |
2. parse postscript commands and return the default response. |
This code does not support other Postscript clients or handling of other |
than the default responses. This may not be sufficient to support your |
printer and client. |
For more information on processing Postscript queries, contact Adobe for their |
technote on this subject *** Get web address *** |
A RIP or printer spooler would have to handle queries in a different manner. |
In order to respond to the queris the spooler code might already have the |
desired response, or may have to delay response until it can query the |
printer to obtain the correct response. |
My thanks to Mark Fleming for his help with debugging this code. |
Written by: Rich Kubota |
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/22/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include <ctype.h> |
#include "StringUtils.h" |
#include "PAPServerSample.h" |
#include "PAPPostScriptStuff.h" |
extern PacketPtr gTempPackPtr; |
char gEOFStr[8] = kEOFStr; |
char gBeginPSStr[8] = "\045\041PS"; // = "%!PS" |
char qBeginQueryStr[8] = kBeginQueryStr; |
char gEndStr[8] = kEndStr; |
char gQueryStr[8] = kQueryStr; |
extern OTLIFO* gFreeQ; |
extern Boolean gDone; |
// prototypes |
Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched, |
SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll); |
/* |
The TestDataIsPSQuery is used to determine whether an incoming packet is the initial packet |
for a postscript query, or is a continuing packet of a prelvious query started with some |
previous packet. |
*/ |
Boolean TestDataIsPSQuery(PacketPtr packetPtr) |
{ |
MyEndpointRef *theEp; |
Boolean result; |
theEp = packetPtr->theEp; |
if (TstInPSQueryFlag(theEp->flags)) // check if the endpoint is already processing a postscript |
result = true; // query for which we have not reached the EndQuery |
else |
{ |
result = IsPacketAPSQuery(packetPtr); |
if (result == true) |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p processing a postscript query;g"); |
#endif |
theEp->prevPtr = nil; // initialize the prevPtr field to nil |
SetInPSQueryFlag(theEp->flags); |
theEp->psState = kLookingForEndStr; // indicate that we have identified query beginning |
// and that the next step is to look for the EndQuery string |
// set the time Data In timestamp field |
BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataIn, sizeof(OTTimeStamp)); |
// reset the numBytesIn field to zero |
theEp->numBytesIn = 0; |
} |
} |
return result; |
} |
/* |
ProcessPSQuery as used in this sample looks for the ps end...query string, then |
gets the response and and sends it |
The routine is designed to be called at deferred task time and uses OTAllocMem |
instead of NewPtr or NewHandle |
Note that this routine is responsible for enqueueing the processed packet ptr |
to the freeQ, unless it appears that there is a partial match to a string that |
we are looking for at the end of the buffer. If so, then the packetPtr is |
save with the endpoint ref to be used when the next incoming packet is processed |
In this routine, the first while loop is where the contents of the ps query are |
analyzed until the end of the packet is read. As each default response is read, |
an individual OTSnd call is made to send the default response. Each response will |
be made with the T_MORE flag bit set to keep the EOF bit from being sent in the |
PAP responses. This routine does not queue multiple responses to send in a single |
response. |
When the end of the packet has been reached, the routine exits the while loop |
and checks for the EOF indicator signaling the end of the query. If the |
EOF is read, then the call is made to SendEmptyPacket, which will result in |
an empty packet with the EOF bit set. The Apple LaserWriter client requires |
receiving a last packet that has the eof bit set to know that the query has been |
completely processed by the printer/server (even if the printer/server has responded |
to all of the query items). |
*/ |
OSStatus ProcessPSQuery(PacketPtr packetPtr) |
{ |
OSStatus err; |
MyEndpointRef *theEp; |
PacketPtr packPtr; |
UInt16 offset; |
UInt16 matchResult; |
Boolean done = false; |
Boolean testflag = false; |
theEp = packetPtr->theEp; |
// add the number of bytes in the packet to the numBytesIn field |
theEp->numBytesIn += packetPtr->numBytes; |
// set the time Data End timestamp field |
BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataEnd, sizeof(OTTimeStamp)); |
err = kOTNoError; |
// check whether we need to concatenate the current and previous packet ptrs |
if ((packetPtr == theEp->prevPtr) || (theEp->prevPtr == nil)) |
{ |
DebugStr("\p no packet to append"); |
testflag = false; |
packPtr = packetPtr; |
} |
else |
{ |
DebugStr("\p checking how we append packets"); |
packPtr = gTempPackPtr; |
// copy contents of the previous packet buffer to the new buffer |
BlockMove((Ptr)theEp->prevPtr, (Ptr)packPtr, sizeof(PacketBuffer)); |
offset = sizeof(PacketBuffer) - kPAPDataSize + (theEp->prevPtr)->numBytes; |
BlockMove((Ptr)&(packetPtr->data), (Ptr)&(packPtr->data[offset]), packetPtr->numBytes); |
// adjust the numbytes field |
packPtr->numBytes += packetPtr->numBytes; |
// enqueue the previous packet ptr back to the gFreeQ since I not expecting a |
// ps default response that overlaps 3 packets. |
// fixed bug here where I release the current packetPtr instead of the previous |
// packetPtr |
OTLIFOEnqueue(gFreeQ, &((theEp->prevPtr)->fLink)); // first field is fLink field |
} |
while (done == false) |
{ |
switch (theEp->psState) |
{ |
case kLookingForEndStr: |
matchResult = FindQueryString(packPtr, kLookingForEndStr); |
break; |
case kLookingForQueryStr: |
matchResult = FindQueryString(packPtr, kLookingForQueryStr); |
break; |
case kLookingForDefaultResponse: |
if (testflag) |
DebugStr("\p about to call ProcessDefaultResponse"); |
matchResult = ProcessDefaultResponse(packPtr); |
break; |
} |
switch (matchResult) |
{ |
case kMatch: |
if (theEp->psState == kLookingForDefaultResponse) |
theEp->psState = kLookingForEndStr; |
else |
theEp->psState += 1; // increment the state |
break; |
case kPartialMatch: |
done = true; // have to come around with the next packet |
break; |
case kNoMatch: |
DebugStr("\p no match"); |
// wasn't able to find a match, not even a |
// partial match, so set the pos to the end of the |
// packet |
packPtr->lastPos = packPtr->numBytes; |
done = true; // have to come around with the next packet |
break; |
} |
} |
// check to see if we see the EOF flag |
// this is the last packet of this query. |
// check to see if the lastPos field is set to the end of the buffer |
// this tels us the we are not awaiting a pending match for a partial |
// string |
if (packPtr->lastPos == packPtr->numBytes) |
{ |
// reset the lastPos field so that we can search for the EOF string as the |
// last few characters |
packPtr->lastPos -= clen(gEOFStr); |
// search for the EOF string at the end of the packet |
matchResult = FindQueryString(packPtr, kLookingForEOFStr); |
// since we reset lastPos, we need to restore it |
packPtr->lastPos = packPtr->numBytes; |
if (matchResult == kMatch) |
{ |
#if SHOW_DEBUG_FLOW |
DebugStr("\p Sending a null packet;g"); |
#endif |
// send an empty response with EOF flag set since this is what the Laserwriter |
// client wants to see. |
SendEmptyPacket(packPtr); |
// clear bit that indicates we are processing a postscript query |
ClrInPSQueryFlag(theEp->flags); |
#if SHOW_DEBUG_FLOW |
DebugStr("\p Have finished ps query;g"); |
#endif |
} |
} |
// check to see whether we processed all of the bytes in the packet. |
if (packPtr->lastPos == packPtr->numBytes) |
{ |
theEp->prevPtr = nil; // if so, then don't save this packet in the prevPtr field. |
// queue the buffer to the freeQ |
OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); // first field is fLink field |
} |
else |
{ |
theEp->prevPtr = packetPtr; // we still have bytes to process. |
//adjust the lastPos field |
packetPtr->lastPos = packetPtr->numBytes - (packPtr->numBytes - packPtr->lastPos); |
} |
return err; |
} |
/* |
DoProcessPSQuery is used as the entry point to processing a postscript query. |
*/ |
Boolean DoProcessPSQuery(PacketPtr packetPtr) |
{ |
OSStatus err = kOTNoError; |
Boolean result = false; |
if (TestDataIsPSQuery(packetPtr)) // is the packet a PS query |
{ |
result = true; // packet is a PostScript query and is being processed |
err = ProcessPSQuery(packetPtr); |
if (err < kOTNoError) |
{ |
DoValueBreak(err, "error occured calling ProcessPSQuery #"); |
// show the error and quit the main event loop |
gDone = true; |
} |
} |
else |
{ |
// we aren't processing PS query packets, so exit this routine |
// the IsPacketQuery routine potentially sets the lastPos field |
// so reset it back to 0; |
packetPtr->lastPos = 0; |
} |
return result; |
} |
/* |
FindString is used to find the iterate through the characters passed in the buffer |
to find the string passed in str. The length of the search string is passed in |
lenStr. pos is a pointer to the index where to start searching. stopChar is the |
last position of the buffer to search |
input |
buffer - pointer to buffer to search |
str - string to match |
lenStr - length of the str to match in buffer |
pos - index where to begin search |
numCharsInBuffer - index of last valid char in buffer |
matchAll - match both upper and lower cases of the input string. |
output - |
lenMatched - number of character which were matched. If no match, then |
returns 0. |
pos - index of the next char to look at for the next search. |
If the str is not found in the buffer between the pos index and the stopChar, |
*pos returns -1, If a partial match is made at the end of the buffer, return the |
negative offset |
*/ |
Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched, |
SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll) |
{ |
SInt16 i; |
SInt16 numToCompare; |
Boolean done = false; |
Boolean ok; |
numToCompare = lenStr; // set the number of chars to compare as the string len. |
while (done == false) // search until the flag is set to true. |
{ |
// check whether the first character matches the character at the |
// current index position |
if ((buffer[*pos] == str[0]) || ((matchAll == true) && (toupper(buffer[*pos]) == |
toupper(str[0])))) |
{ |
if (numCharsInBuffer < (*pos + lenStr)) |
{ |
// the has too few remaining characters to compare all of the current |
// string. |
numToCompare = numCharsInBuffer - *pos; |
} |
//init OK boolean |
ok = true; |
for (i = 1; (i < numToCompare) && ok; i++) |
{ |
if ((buffer[*pos+i] == str[i]) || ((matchAll == true) && |
(toupper(buffer[*pos+1]) == toupper(str[i])))) |
continue; |
else |
ok = false; |
} |
// we've done our testing, now see if we found a match |
if (ok == true) // was match found |
{ |
*lenMatched = numToCompare; // return the number of chars matched |
*pos += numToCompare; // return the next position in the buffer to start next search |
done = true; // search finished. |
} |
} |
if (done == false) |
{ |
*pos += 1; // no match found so increment the place to start the next search iteration |
if ((*pos+1) == numCharsInBuffer) |
{ |
// no more characters in the buffer to search |
*lenMatched = 0; // set len matched to nothing |
done = true; // search finished. |
} |
} |
} |
return (*lenMatched != 0); |
} |
/* |
IsPacketAPSQuery is used to check whether the contents of a packet are the beginning of |
a PostScript query. This can be found since there will be the signature stuff |
at the beginning of the query. |
*/ |
Boolean IsPacketAPSQuery(PacketPtr packetPtr) |
{ |
SInt16 lenMatched, pos; |
Boolean result; |
pos = 0; // start search from the beginning of the buffer |
result = FindString((char*)&(packetPtr->data), gBeginPSStr, clen(gBeginPSStr), |
&lenMatched, &pos, 4, kCaseMatchAll); // must find the "%!PS" string in position 3 & 4 of the buffer |
if (result == true) |
{ |
// the search found something in the first 4 characters of the buffer |
if (lenMatched < clen(gBeginPSStr)) |
{ |
result = false; // didn't match 2 characters |
} |
else |
{ |
result = FindString((char*)&(packetPtr->data), qBeginQueryStr, |
clen(qBeginQueryStr), &lenMatched, |
&pos, 24, kCaseMatchAll); // must find "Query\015" in pos 15 - 20 of the buffer |
if (result == true) |
{ |
if (lenMatched < clen(qBeginQueryStr)) |
result = false; |
else |
{ |
// we're going to return a true result, to set the packetPtr.lastPos |
// field to the current pos field. When we start our next search, we |
// do so at this position. |
packetPtr->lastPos = pos; |
} |
} |
} |
} |
return result; |
} |
UInt16 FindQueryString(PacketPtr packetPtr, SInt16 whichStr) |
{ |
SInt16 lenMatched, pos; |
UInt16 result1, len; |
Boolean result; |
char *str; |
result1 = kNoMatch; // initialize to no Match |
pos = packetPtr->lastPos; // start search from where we last stopped looking in buffer |
// first find the query prefix |
switch (whichStr) |
{ |
case kLookingForEndStr: |
str = gEndStr; |
len = clen(gEndStr); |
break; |
case kLookingForQueryStr: |
str = gQueryStr; |
len = clen(gQueryStr); |
break; |
case kLookingForEOFStr: |
str = gEOFStr; |
len = clen(gEOFStr); |
break; |
} |
result = FindString((char*)&(packetPtr->data), str, len, |
&lenMatched, &pos, packetPtr->numBytes, kCaseMustMatch); |
if (result == true) |
{ |
// check that the proper string len was returned |
if (lenMatched < len) |
{ |
// we have reached the end of the buffer and have found a partial match. |
result1 = kPartialMatch; |
// set the lastPos field to where the partial match begins |
packetPtr->lastPos = pos - lenMatched; |
} |
else |
{ |
// all parts found |
result1 = kMatch; |
// complete match found, so advance the lastPos field |
packetPtr->lastPos = pos; |
} |
} |
return result1; |
} |
UInt16 ProcessDefaultResponse(PacketPtr packetPtr) |
{ |
OTResult otErr; |
UInt32 pos, begin; |
UInt16 result; |
char response[255]; |
// since we've found the end of the query chars, then there is at least a |
// partial match |
result = kPartialMatch; |
// set pos to the lastPos position since this is the beginning of |
// the query |
begin = pos = packetPtr->lastPos; |
// remove the leading space character which may precede the default response |
while (packetPtr->data[pos] == kSpaceChar) |
{ |
pos++; |
begin++; |
} |
// gather the default response characters |
while ((packetPtr->data[pos] != kReturnChar) && (pos < packetPtr->numBytes)) |
{ |
// gather default response characters |
response[pos - begin] = packetPtr->data[pos]; |
// increment the counter |
pos++; |
} |
// go ahead and set the terminating null character for the "c" string |
if ((pos - begin) < 255) |
response[pos - begin] = 0; |
else |
{ |
// this should never happen, but if it does, I want to know about it. |
#if SHOW_DEBUG_FLOW |
// DebugStr((const unsigned char *)"\p response greater than 255 chars"); |
#endif |
response[255] = 0; |
} |
if (packetPtr->data[pos] == kReturnChar) |
{ |
// we have found the end of the default response |
// catenate a line feed character to the response |
ccatchr(response, kLineFeedChar, 1); |
otErr = OTSnd((packetPtr->theEp)->ep, response, clen(response), T_MORE); |
if (otErr < 0) |
{ |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)otErr, "error occured sending PS response #"); |
#endif |
} |
packetPtr->lastPos = pos; |
result = kMatch; |
} |
#if SHOW_DEBUG_FLOW |
ccatchr(response, ';', 1); |
ccatchr(response, 'g', 1); |
c2p(response); |
// DebugStr((const unsigned char*)response); |
#endif |
return result; |
} |
/* |
SendEmptyPacket is implemented to send an empty packet with the T_MORE flag not set. |
The Apple LaserWriter client requires that the last response to a Postscript query |
have the EOF flag set. By having the EOM option enabled and sending a packet with |
the T_MORE flag not set, OT/PAP will send an empty packet that has the EOF bit set. |
*/ |
void SendEmptyPacket(PacketPtr packetPtr) |
{ |
OTResult err; |
char response[16]; |
err = OTSnd((packetPtr->theEp)->ep, &response, 0, 0); |
if (err != kOTNoError) |
{ |
#if SHOW_DEBUG_FLOW |
DoValueBreak((long)err, "error occured sending empty packet #"); |
#endif |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22