1248 lines
26 KiB
C
1248 lines
26 KiB
C
/*
|
|
** Copyright(c) Microsoft Corp., 1991
|
|
*
|
|
|
|
/*
|
|
** File Name:
|
|
**
|
|
** PSPQUERY.C - PostScript Parser Handlers for Query Comments
|
|
**
|
|
** General Description:
|
|
**
|
|
** These are the routines that parse and interprete the PostScript data
|
|
** stream. This interpreter looks for PostScript Document Structuring
|
|
** Comments, which are the spooler commands that are imbedded in the
|
|
** PostScript job stream. This particular file has the code that handles
|
|
** the postscript query commands
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <search.h>
|
|
|
|
#include <windows.h>
|
|
#include <macps.h>
|
|
#include <psqfont.h>
|
|
#include <debug.h>
|
|
#include <pskey.h>
|
|
|
|
DWORD HandleFeatureLanguage(PJR pjr);
|
|
DWORD HandleFeatureVersion(PJR pjr);
|
|
DWORD HandleFeatureBinary(PJR pjr);
|
|
DWORD HandleFeatureProduct(PJR pjr);
|
|
DWORD HandleFeatureResolution(PJR pjr);
|
|
DWORD HandleFeatureColor(PJR pjr);
|
|
DWORD HandleFeatureVM(PJR pjr);
|
|
DWORD HandleFeatureSpooler(PJR pjr);
|
|
DWORD HandleBeginFeatureQuery(PJR pjr, PSZ pszQuery);
|
|
DWORD HandleEndFeatureQuery(PJR pjr, PSZ pszDefaultResponse);
|
|
BOOL IsFontAvailable(PQR pqr, LPSTR pszFontName);
|
|
int __cdecl compare(const void * arg1, const void * arg2);
|
|
LONG GetFontListResponse(PQR pqr, LPSTR pFontBuffer, DWORD cbFontBuffer, LPDWORD pcbNeeded);
|
|
|
|
|
|
/*
|
|
** HandleEndFontListQuery()
|
|
**
|
|
** Purpose: Handles the EndFontListQuery Comment
|
|
**
|
|
** Returns: Error Codes from PAPWrite Call
|
|
**
|
|
*/
|
|
|
|
#define DEFAULT_FONTBUF_SIZE 2048
|
|
|
|
DWORD
|
|
HandleEndFontListQuery(
|
|
PJR pjr
|
|
)
|
|
{
|
|
PQR pqr = pjr->job_pQr;
|
|
LPSTR pFontBuffer = NULL;
|
|
LPSTR pFontWalker = NULL;
|
|
DWORD cbFontBuffer = 0;
|
|
DWORD dwStatus = NO_ERROR;
|
|
DWORD cbNeeded;
|
|
|
|
DBGPRINT(("Enter HandleEndFontListQuery\n"));
|
|
|
|
do
|
|
{
|
|
//
|
|
// allocate a typical font buffer
|
|
//
|
|
|
|
if ((pFontBuffer = (LPSTR)LocalAlloc(LPTR, DEFAULT_FONTBUF_SIZE)) == NULL)
|
|
{
|
|
dwStatus = GetLastError();
|
|
DBGPRINT(("ERROR: unable to allocate font buffer\n"));
|
|
break;
|
|
}
|
|
cbFontBuffer = DEFAULT_FONTBUF_SIZE;
|
|
|
|
//
|
|
// get the fontlist response
|
|
//
|
|
if ((dwStatus = GetFontListResponse(pqr, pFontBuffer, cbFontBuffer, &cbNeeded)) != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// if buffer too small, reallocate and try again
|
|
//
|
|
|
|
if (dwStatus == ERROR_MORE_DATA)
|
|
{
|
|
LocalFree(pFontBuffer);
|
|
if ((pFontBuffer = (LPSTR)LocalAlloc(LPTR, cbNeeded)) == NULL)
|
|
{
|
|
dwStatus = GetLastError();
|
|
DBGPRINT(("ERROR: unable to reallocate font buffer\n"));
|
|
break;
|
|
}
|
|
cbFontBuffer = cbNeeded;
|
|
|
|
if ((dwStatus = GetFontListResponse(pqr, pFontBuffer, cbFontBuffer, &cbNeeded)) != ERROR_SUCCESS)
|
|
{
|
|
DBGPRINT(("ERROR: unable to get font list response\n"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// send response to client (in single font name per write)
|
|
// NOTE: While the Apple LaserWriter driver gets fonts from
|
|
// the printer in 512 byte packets that are packed with multiple
|
|
// font names, the PageMaker driver expects fonts to come in
|
|
// a single font per write scheme. So we lose the work that builds
|
|
// a font response like the Mac LaserWriter driver by sending
|
|
// the fonts as PageMaker expects them (which works for both
|
|
// drivers)
|
|
//
|
|
|
|
DBGPRINT(("writing fontlist:\n%s", pFontBuffer));
|
|
pFontWalker = pFontBuffer;
|
|
|
|
cbFontBuffer = 0;
|
|
|
|
while (*pFontWalker != '*')
|
|
{
|
|
cbFontBuffer = strlen(pFontWalker);
|
|
if ((dwStatus = TellClient(pjr, FALSE, pFontWalker, cbFontBuffer)) != NO_ERROR)
|
|
{
|
|
|
|
//
|
|
// error sending data to client
|
|
//
|
|
|
|
DBGPRINT(("ERROR: unable to send font to client\n"));
|
|
break;
|
|
}
|
|
pFontWalker += (cbFontBuffer + 1);
|
|
}
|
|
|
|
//
|
|
// do not fail if a send of a font fails. If we can get the
|
|
// termination font out, the Mac will just download any fonts
|
|
// it needs and the job will print - albeit slowly.
|
|
//
|
|
|
|
if ((dwStatus = TellClient(pjr, pjr->EOFRecvd, pFontWalker, strlen(pFontWalker))) != NO_ERROR)
|
|
{
|
|
DBGPRINT(("ERROR: unable to send terminating font to client\n"));
|
|
break;
|
|
}
|
|
} while (FALSE);
|
|
|
|
if (pFontBuffer != NULL)
|
|
{
|
|
LocalFree (pFontBuffer);
|
|
}
|
|
|
|
return dwStatus;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetFontListResponse - formats a fontlist buffer to send to a Mac
|
|
//
|
|
// Based on the queue type (Postscript or non), a fontlist is generated
|
|
// and placed in the supplied buffer. The font list is an ordered list
|
|
// of fonts separated by '\n\0' with a terminating font of '*\n\0'.
|
|
//
|
|
// if the buffer is too small, this routine returns ERROR_MORE_DATA.
|
|
// if for some other reason the list cannot be generated, the return
|
|
// value is ERROR_INVALID_PARAMETER.
|
|
// if the function successfully returns a font list, the return value
|
|
// is ERROR_SUCCESS.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
LONG
|
|
GetFontListResponse(
|
|
PQR pqr,
|
|
LPSTR pFontBuffer,
|
|
DWORD cbFontBuffer,
|
|
LPDWORD pcbNeeded
|
|
)
|
|
{
|
|
LONG lReturn = ERROR_SUCCESS;
|
|
HANDLE hFontQuery = INVALID_HANDLE_VALUE;
|
|
DWORD cFonts;
|
|
DWORD dwIndex;
|
|
BOOL boolPSQueue;
|
|
LPSTR *apszFontNames = NULL;
|
|
LPSTR pTempBuffer = NULL;
|
|
DWORD cbTempBuffer = cbFontBuffer;
|
|
DWORD cbFontFileName;
|
|
LPSTR pFont;
|
|
DWORD cbFont;
|
|
DWORD rc;
|
|
|
|
DBGPRINT(("enter GetFontListResponse(cbBuffer:%d, cbNeeded:%d\n", cbFontBuffer, *pcbNeeded));
|
|
|
|
do
|
|
{
|
|
//
|
|
// what kind of queue are we
|
|
//
|
|
if (wcscmp(pqr->pDataType, MACPS_DATATYPE_RAW))
|
|
{
|
|
//
|
|
// we are PSTODIB
|
|
//
|
|
boolPSQueue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we are Postscript
|
|
//
|
|
boolPSQueue = TRUE;
|
|
}
|
|
|
|
//
|
|
// allocate an array of fontname pointers.
|
|
//
|
|
|
|
if (boolPSQueue)
|
|
{
|
|
cFonts = pqr->MaxFontIndex + 1;
|
|
DBGPRINT(("cFonts=%d\n", cFonts));
|
|
apszFontNames = (LPSTR*)LocalAlloc(LPTR, cFonts * sizeof(LPSTR));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// for PSTODIB we will need a temp buffer for the fonts as well
|
|
//
|
|
if ((pTempBuffer = (LPSTR)LocalAlloc(LPTR, cbFontBuffer)) == NULL)
|
|
{
|
|
lReturn = ERROR_INVALID_PARAMETER;
|
|
DBGPRINT(("ERROR: unable to allocate temp font buffer\n"));
|
|
break;
|
|
}
|
|
|
|
if ((rc = PsBeginFontQuery(&hFontQuery)) != PS_QFONT_SUCCESS)
|
|
{
|
|
DBGPRINT(("ERROR: PsBeginFontQuery returns %d\n", rc));
|
|
lReturn = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if ((rc = PsGetNumFontsAvailable(hFontQuery,
|
|
&cFonts)) != PS_QFONT_SUCCESS)
|
|
{
|
|
DBGPRINT(("ERROR: PsGetNumFontsAvailable returns %d\n", rc));
|
|
lReturn = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
apszFontNames = (LPSTR*)LocalAlloc(LPTR, cFonts * sizeof(LPSTR));
|
|
}
|
|
|
|
if (apszFontNames == NULL)
|
|
{
|
|
DBGPRINT(("ERROR: cannot allocate font list array\n"));
|
|
lReturn = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// fill the array of fontname pointers
|
|
//
|
|
|
|
*pcbNeeded = 3;
|
|
pFont = pTempBuffer;
|
|
for (dwIndex = 0; dwIndex < cFonts; dwIndex++)
|
|
{
|
|
if (boolPSQueue)
|
|
{
|
|
apszFontNames[dwIndex] = pqr->fonts[dwIndex].name;
|
|
*pcbNeeded += (strlen(pqr->fonts[dwIndex].name)+2);
|
|
DBGPRINT(("adding font:%s, cbNeeded:%d, index:%d\n", pqr->fonts[dwIndex].name, *pcbNeeded, dwIndex));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// pstodib - add the font to the temp buffer
|
|
// and set the pointer
|
|
//
|
|
cbFont = cbTempBuffer = cbFontBuffer;
|
|
if ((rc = PsGetFontInfo(hFontQuery,
|
|
dwIndex,
|
|
pFont,
|
|
&cbFont,
|
|
NULL,
|
|
&cbFontFileName)) != PS_QFONT_SUCCESS)
|
|
{
|
|
//
|
|
// if we are out of memory, continue enumeration
|
|
// to get size needed, but set return to ERROR_MORE_DATA
|
|
//
|
|
if (rc == PS_QFONT_ERROR_FONTNAMEBUFF_TOSMALL)
|
|
{
|
|
DBGPRINT(("user buffer too small for font query\n"));
|
|
lReturn = ERROR_MORE_DATA;
|
|
pFont = pTempBuffer;
|
|
cbFont = cbTempBuffer = cbFontBuffer;
|
|
if ((rc = PsGetFontInfo(hFontQuery,
|
|
dwIndex,
|
|
pFont,
|
|
&cbFont,
|
|
NULL,
|
|
&cbFontFileName)) != PS_QFONT_SUCCESS)
|
|
{
|
|
//
|
|
// we be hosed. Fail.
|
|
//
|
|
lReturn = ERROR_INVALID_PARAMETER;
|
|
DBGPRINT(("ERROR: cannot continue PSTODIB font enumeration\n"));
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*pcbNeeded += cbFont + 2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pcbNeeded += cbFont + 2;
|
|
}
|
|
apszFontNames[dwIndex] = pFont;
|
|
cbTempBuffer -= cbFont;
|
|
pFont += cbFont;
|
|
cbFont = cbTempBuffer;
|
|
}
|
|
|
|
}
|
|
|
|
if (*pcbNeeded > cbFontBuffer)
|
|
{
|
|
lReturn = ERROR_MORE_DATA;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// build the fontlistresponse
|
|
//
|
|
|
|
cbFontBuffer = 0;
|
|
for (dwIndex = 0; dwIndex < cFonts; dwIndex++)
|
|
{
|
|
cbFont = sprintf(pFontBuffer, "%s\n", apszFontNames[dwIndex]) + 1;
|
|
pFontBuffer += cbFont;
|
|
cbFontBuffer += cbFont;
|
|
}
|
|
|
|
memcpy (pFontBuffer, "*\n", 3);
|
|
} while (FALSE);
|
|
|
|
if (apszFontNames != NULL)
|
|
{
|
|
LocalFree(apszFontNames);
|
|
}
|
|
|
|
if (pTempBuffer != NULL)
|
|
{
|
|
LocalFree(pTempBuffer);
|
|
}
|
|
|
|
if (hFontQuery != INVALID_HANDLE_VALUE)
|
|
{
|
|
PsEndFontQuery(hFontQuery);
|
|
}
|
|
|
|
return (lReturn);
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
compare(const void* arg1, const void* arg2)
|
|
{
|
|
return _stricmp(* (char **)arg1, * (char **)arg2);
|
|
}
|
|
|
|
|
|
//
|
|
// For Postscript printers, the font enumeration technique is complex.
|
|
// EnumFontFamilies expects the programmer to specify a callback function
|
|
// that will be called either once for every font family, or once for
|
|
// every font face in a family. To get all fonts available, I use
|
|
// EnumFontFamilies twice. The first enumeration, I call EnumFontFamilies
|
|
// with a null value for the family name. This causes the callback
|
|
// function to be called once for each family name installed. This
|
|
// callback function then does an enumeration on that family name to
|
|
// get the specific face names in the family. This second layer of
|
|
// enumeration specifies yet another callback function that returns
|
|
// the font name to the Macintosh client.
|
|
//
|
|
void
|
|
EnumeratePostScriptFonts(
|
|
PJR pjr
|
|
)
|
|
{
|
|
PQR pqr = pjr->job_pQr;
|
|
|
|
DBGPRINT(("ENTER EnumeratePostScriptFonts\n"));
|
|
|
|
if (pjr->hicFontFamily != NULL)
|
|
{
|
|
//
|
|
// enumerate the font families
|
|
//
|
|
EnumFontFamilies(pjr->hicFontFamily,
|
|
NULL,
|
|
(FONTENUMPROC)FamilyEnumCallback,
|
|
(LPARAM)pjr);
|
|
}
|
|
}
|
|
|
|
|
|
int CALLBACK
|
|
FamilyEnumCallback(
|
|
LPENUMLOGFONT lpelf,
|
|
LPNEWTEXTMETRIC pntm,
|
|
int iFontType,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
PQR pqr = ((PJR)lParam)->job_pQr;
|
|
PJR pjr = (PJR)lParam;
|
|
|
|
DBGPRINT(("Enter FamilyEnumCallback for family %ws\n", lpelf->elfFullName));
|
|
|
|
//
|
|
// enumerate the fonts in this family
|
|
//
|
|
|
|
if (iFontType & DEVICE_FONTTYPE)
|
|
{
|
|
DBGPRINT(("enumerating face names\n"));
|
|
EnumFontFamilies(pjr->hicFontFace,
|
|
lpelf->elfFullName,
|
|
(FONTENUMPROC)FontEnumCallback,
|
|
lParam);
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(("this family is not a DEVICE_FONTTYPE\n"));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int CALLBACK
|
|
FontEnumCallback(
|
|
LPENUMLOGFONT lpelf,
|
|
LPNEWTEXTMETRIC pntm,
|
|
int iFontType,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
DWORD PAPStatus;
|
|
PJR pjr = (PJR)lParam;
|
|
BYTE pszFontName[255];
|
|
|
|
DBGPRINT(("Enter FontEnumCallback\n"));
|
|
|
|
//
|
|
// return this font name to the client
|
|
//
|
|
|
|
if (iFontType & DEVICE_FONTTYPE)
|
|
{
|
|
CharToOem(lpelf->elfFullName, pszFontName);
|
|
if (PAPStatus = TellClient(pjr,
|
|
FALSE,
|
|
pszFontName,
|
|
strlen(pszFontName)))
|
|
{
|
|
DBGPRINT(("ERROR: TellClient returns %d\n", PAPStatus));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGPRINT(("%ws is not a DEVICE_FONTTYPE\n", lpelf->elfFullName));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
** HandleEndQuery()
|
|
**
|
|
** Purpose: PageMaker will send a query that goes like this:
|
|
**
|
|
** %%BeginQuery
|
|
** ...
|
|
** %%EndQuery (spooler)
|
|
**
|
|
** In order to allow pagemaker to print TIFF formated images
|
|
** properly, we should respond to this query with "printer".
|
|
**
|
|
** Returns: Error Codes from PAPWrite Call
|
|
**
|
|
*/
|
|
DWORD
|
|
HandleEndQuery(
|
|
PJR pjr,
|
|
PBYTE ps
|
|
)
|
|
{
|
|
char *token;
|
|
CHAR pszResponse[PSLEN+1];
|
|
|
|
DBGPRINT(("Enter HandleEndQuery\n"));
|
|
token = strtok(NULL,"\n");
|
|
|
|
if (token == NULL)
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
/* strip off any leading blanks in the default */
|
|
token += strspn(token, " ");
|
|
|
|
//
|
|
// respond with the default
|
|
//
|
|
|
|
sprintf(pszResponse, "%s\x0a", token);
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
** FinishDefaultQuery()
|
|
**
|
|
** Purpose: Scans for the PostScript command specified in psKeyWord. It
|
|
** then will respond with the default response specified on that
|
|
** line. It will set the InProgress field in the JOB_RECORD to
|
|
** an InProgress value if the default is not found in this buffer.
|
|
**
|
|
** Returns: Error Codes from PAPWrite Call
|
|
**
|
|
***************************************************************************/
|
|
DWORD
|
|
FinishDefaultQuery(
|
|
PJR pjr,
|
|
PBYTE ps
|
|
)
|
|
{
|
|
char * token;
|
|
char buf[PSLEN+1];
|
|
|
|
DBGPRINT(("FinishDefaultQuery: %s\n", ps));
|
|
|
|
if (NULL == (token = strtok (ps," :")))
|
|
{
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
pjr->InProgress= NOTHING;
|
|
|
|
/* First We Should Handle the cases that do not use the default response */
|
|
|
|
if (!_stricmp(token, EFEATUREQUERY))
|
|
return (HandleEndFeatureQuery(pjr, strtok (NULL," \n")));
|
|
|
|
if (!_stricmp(token, EFONTLISTQ))
|
|
return( HandleEndFontListQuery (pjr));
|
|
|
|
if (!_stricmp(token, EQUERY))
|
|
return( HandleEndQuery (pjr, ps));
|
|
|
|
if (!_stricmp(token, EPRINTERQUERY))
|
|
return( HandleEndPrinterQuery(pjr));
|
|
|
|
if (!_stricmp(token, EVMSTATUS))
|
|
{
|
|
sprintf(buf, "%ld", pjr->job_pQr->FreeVM);
|
|
return (TellClient(pjr, pjr->EOFRecvd, buf , strlen(buf)));
|
|
}
|
|
|
|
if ((token = strtok(NULL,"\n")) == NULL)
|
|
{
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
/* strip off any leading blanks in the default. Append a LF */
|
|
token += strspn(token, " ");
|
|
sprintf(buf, "%s\x0a", token);
|
|
return (TellClient(pjr, pjr->EOFRecvd, buf, strlen(buf)));
|
|
}
|
|
|
|
|
|
DWORD
|
|
HandleEndFeatureQuery(
|
|
PJR pjr,
|
|
PSZ pszDefaultResponse)
|
|
{
|
|
|
|
DWORD rc = NO_ERROR;
|
|
CHAR pszResponse[PSLEN];
|
|
|
|
DBGPRINT(("enter HandleEndFeatureQuery\n"));
|
|
|
|
do
|
|
{
|
|
//
|
|
// return the default response if there is one
|
|
//
|
|
if (NULL != pszDefaultResponse)
|
|
{
|
|
sprintf(pszResponse, "%s\x0a", pszDefaultResponse);
|
|
DBGPRINT(("responding with default response from query: %s\n", pszResponse));
|
|
rc = TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse));
|
|
break;
|
|
}
|
|
|
|
DBGPRINT(("responding with Unknown\n"));
|
|
rc = TellClient(pjr, pjr->EOFRecvd, DEFAULTRESPONSE, strlen(DEFAULTRESPONSE));
|
|
|
|
} while (FALSE);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** Routine:
|
|
** ParseDict
|
|
**
|
|
** Purpse:
|
|
**
|
|
** This routine will take a given QueryProcSet, BeginProcSet, or
|
|
** IncludeProcSet comment and determine what dictionary is being
|
|
** referenced.
|
|
**
|
|
** Entry:
|
|
**
|
|
** Address of a record to fill in with the Dictionary information.
|
|
**
|
|
** Exit:
|
|
**
|
|
** Filed in structure
|
|
**
|
|
*/
|
|
void
|
|
FindDictVer(
|
|
PDR pdr
|
|
)
|
|
{
|
|
char *token;
|
|
|
|
pdr->name[0] = 0;
|
|
pdr->version[0] = 0;
|
|
pdr->revision[0] = 0;
|
|
|
|
DBGPRINT(("Enter FindDictVer\n"));
|
|
|
|
/* lets look for a line like this: "(appledict md)" 67 0 */
|
|
token = strtok(NULL,"() \""); /* this should be appledict */
|
|
|
|
if (token !=NULL)
|
|
{
|
|
/*/
|
|
** If the token is "Appledict", then we need to parse again to get
|
|
** the real dict name.
|
|
*/
|
|
if (!_stricmp(token, APPLEDICTNAME))
|
|
token = strtok(NULL,"() \""); /* this sholud be md, or some other dict name */
|
|
|
|
if (token != NULL)
|
|
{
|
|
strcpy(pdr->name, token);
|
|
token = strtok(NULL," \"");
|
|
|
|
if (token != NULL)
|
|
{
|
|
strcpy(pdr->version,token);
|
|
token = strtok(NULL," \"");
|
|
|
|
if (token != NULL)
|
|
strcpy(pdr->revision,token);
|
|
}
|
|
}
|
|
}
|
|
DBGPRINT(("FindDictVer: %s:%s:%s\n", pdr->name,
|
|
pdr->version, pdr->revision));
|
|
} // End of FindDictVer
|
|
|
|
|
|
|
|
struct commtable
|
|
{
|
|
PSZ commentstr;
|
|
DWORD (*pfnHandle)(PJR, PSZ);
|
|
PSZ parmstr;
|
|
} qrytable [] =
|
|
{
|
|
{ BPROCSETQUERY, HandleBeginProcSetQuery, NULL },
|
|
{ BFONTQUERY, HandleBeginFontQuery, NULL },
|
|
{ NULL, NULL, NULL }
|
|
};
|
|
|
|
|
|
/*
|
|
**
|
|
** HandleBQCommentEvent()
|
|
**
|
|
** Purpose: Handles Begin Query Comment Events.
|
|
**
|
|
** Returns: Error Codes
|
|
*/
|
|
DWORD
|
|
HandleBQComment(
|
|
PJR pjr,
|
|
PBYTE ps
|
|
)
|
|
{
|
|
PSZ token;
|
|
PSZ qrytoken;
|
|
PSZ endquery = EQCOMMENT;
|
|
DWORD status = NO_ERROR;
|
|
struct commtable *pct;
|
|
|
|
DBGPRINT(("Enter HandleBQComment\n"));
|
|
|
|
//
|
|
// Parse the keyword
|
|
//
|
|
if ((token= strtok(ps," :")) != NULL)
|
|
{
|
|
DBGPRINT(("query: %s\n", token));
|
|
|
|
// found the keyword, call the correct handler
|
|
for (pct = qrytable; pct->pfnHandle != NULL; pct++)
|
|
{
|
|
if (!strcmp(token, pct->commentstr))
|
|
{
|
|
status = pct->pfnHandle(pjr,
|
|
pct->parmstr == NULL ? ps : pct->parmstr);
|
|
if (status == (DWORD)-1) // Special error code, handle it the default way
|
|
{
|
|
status = NO_ERROR;
|
|
break;
|
|
}
|
|
return (status);
|
|
}
|
|
}
|
|
|
|
// special case the BeginFeatureQuery comment as the item
|
|
// being queried comes as the next token
|
|
if (!strcmp(token, BFEATUREQUERY))
|
|
{
|
|
status = HandleBeginFeatureQuery(pjr, strtok(NULL," \n\x09"));
|
|
return (status);
|
|
}
|
|
|
|
// special case the BeginQuery comment for the same reasons
|
|
// as BeginFeatureQuery
|
|
if (!strcmp(token, BQUERY))
|
|
{
|
|
qrytoken = strtok(NULL, " \n\x09");
|
|
if (NULL != qrytoken)
|
|
{
|
|
status = HandleBeginFeatureQuery(pjr, qrytoken);
|
|
return (status);
|
|
}
|
|
}
|
|
|
|
// keyword not recognized, parse as unknown comment. Token is
|
|
// of form %%?BeginXXXXQuery. Change this to the form %%?EndXXXXQuery
|
|
// and pass it to HandleBeginXQuery.
|
|
token += sizeof(BQCOMMENT) - sizeof(EQCOMMENT);
|
|
strncpy(token, EQCOMMENT, sizeof(EQCOMMENT)-1);
|
|
HandleBeginXQuery(pjr, token);
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
|
|
|
|
struct featurecommtable
|
|
{
|
|
PSZ commentstr;
|
|
DWORD (*pfnHandle)(PJR);
|
|
} featureqrytable [] =
|
|
{
|
|
{ FQLANGUAGELEVEL, HandleFeatureLanguage },
|
|
{ FQPSVERSION, HandleFeatureVersion },
|
|
{ FQBINARYOK, HandleFeatureBinary },
|
|
{ FQPRODUCT, HandleFeatureProduct },
|
|
{ FQPRODUCT1, HandleFeatureProduct },
|
|
{ FQRESOLUTION, HandleFeatureResolution },
|
|
{ FQCOLORDEVICE, HandleFeatureColor },
|
|
{ FQFREEVM, HandleFeatureVM },
|
|
{ FQTOTALVM, HandleFeatureVM },
|
|
{ FQSPOOLER, HandleFeatureSpooler },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
DWORD
|
|
HandleBeginFeatureQuery(
|
|
PJR pjr,
|
|
PSZ pszQuery
|
|
)
|
|
{
|
|
DWORD i, rc = NO_ERROR;
|
|
struct featurecommtable *pct;
|
|
|
|
DBGPRINT(("enter HandleBeginFeatureQuery:%s\n", pszQuery));
|
|
|
|
do
|
|
{
|
|
//
|
|
// if we have no query keyword, break;
|
|
//
|
|
|
|
if (NULL == pszQuery)
|
|
{
|
|
DBGPRINT(("NULL feature\n"));
|
|
break;
|
|
}
|
|
|
|
// Strip out any trailing CR/LF before comparing
|
|
for (i = strlen(pszQuery) - 1; ; i--)
|
|
{
|
|
if ((pszQuery[i] != CR) && (pszQuery[i] != LINEFEED))
|
|
break;
|
|
pszQuery[i] = 0;
|
|
}
|
|
//
|
|
// walk the list of known feature queries and call the appropriate
|
|
// feature query handler
|
|
//
|
|
|
|
for (pct = featureqrytable; pct->pfnHandle != NULL; pct++)
|
|
{
|
|
if (!strcmp(pszQuery, pct->commentstr))
|
|
{
|
|
rc = pct->pfnHandle(pjr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (NULL == pct->pfnHandle)
|
|
{
|
|
DBGPRINT(("WARNING: feature query not found\n"));
|
|
pjr->InProgress = QUERYDEFAULT;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
HandleFeatureLanguage(
|
|
PJR pjr
|
|
)
|
|
{
|
|
CHAR pszResponse[PSLEN];
|
|
//
|
|
// this routine should respond with the PostScript language level
|
|
// supported by the printer. The response is in the form "<level>"
|
|
// where <level> is PostScript language level - either a 1 or a 2 at
|
|
// the time of this writing.
|
|
//
|
|
|
|
DBGPRINT(("enter HandleFeatureLanguage\n"));
|
|
|
|
sprintf(pszResponse, "\"%s\"\x0a", pjr->job_pQr->pszLanguageLevel);
|
|
DBGPRINT(("responding with:%s\n", pszResponse));
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
}
|
|
|
|
DWORD
|
|
HandleFeatureVersion(
|
|
PJR pjr
|
|
)
|
|
{
|
|
CHAR pszResponse[PSLEN];
|
|
|
|
DBGPRINT(("enter HandleFeatureVersion\n"));
|
|
|
|
sprintf(pszResponse, "\"(%s) %s\"\x0a", pjr->job_pQr->Version, pjr->job_pQr->Revision);
|
|
DBGPRINT(("responding with:%s\n", pszResponse));
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
}
|
|
|
|
DWORD
|
|
HandleFeatureBinary(
|
|
PJR pjr
|
|
)
|
|
{
|
|
DBGPRINT(("enter HandleFeatureBinary\n"));
|
|
|
|
return (TellClient(pjr,
|
|
pjr->EOFRecvd,
|
|
pjr->job_pQr->SupportsBinary ? "True\x0a" : "False\x0a",
|
|
pjr->job_pQr->SupportsBinary ? 5: 6));
|
|
}
|
|
|
|
DWORD
|
|
HandleFeatureProduct(
|
|
PJR pjr
|
|
)
|
|
{
|
|
CHAR pszResponse[PSLEN];
|
|
|
|
DBGPRINT(("enter HandleFeatureProduct\n"));
|
|
|
|
sprintf(pszResponse, "\"(%s)\"\x0a", pjr->job_pQr->Product);
|
|
DBGPRINT(("responding with:%s\n", pszResponse));
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
HandleFeatureResolution(
|
|
PJR pjr
|
|
)
|
|
{
|
|
CHAR pszResponse[PSLEN];
|
|
|
|
DBGPRINT(("enter HandleFeatureResolution\n"));
|
|
|
|
sprintf(pszResponse, "%s\x0a", pjr->job_pQr->pszResolution);
|
|
DBGPRINT(("responding with:%s\n", pszResponse));
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
}
|
|
|
|
|
|
DWORD
|
|
HandleFeatureColor (PJR pjr)
|
|
{
|
|
CHAR pszResponse[PSLEN];
|
|
|
|
DBGPRINT(("enter HandleFeatureColor\n"));
|
|
|
|
sprintf(pszResponse, "%s\x0a", pjr->job_pQr->pszColorDevice);
|
|
DBGPRINT(("responding with:%s\n", pszResponse));
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
}
|
|
|
|
DWORD
|
|
HandleFeatureVM(
|
|
PJR pjr
|
|
)
|
|
{
|
|
CHAR pszResponse[PSLEN];
|
|
|
|
DBGPRINT(("enter HandleFeatureVM\n"));
|
|
|
|
sprintf(pszResponse, "\"%d\"\x0a", pjr->job_pQr->FreeVM);
|
|
DBGPRINT(("responding with:%s\n", pszResponse));
|
|
return (TellClient(pjr, pjr->EOFRecvd, pszResponse, strlen(pszResponse)));
|
|
}
|
|
|
|
DWORD
|
|
HandleFeatureSpooler(
|
|
PJR pjr
|
|
)
|
|
{
|
|
DBGPRINT(("enter HandleFeatureSpooler\n"));
|
|
return (TellClient(pjr, pjr->EOFRecvd, "1 \x0a", 3));
|
|
}
|
|
|
|
|
|
/*
|
|
** HandleBeginProcSetQuery()
|
|
**
|
|
** Purpose: Handles BeginProcSetQuery Comment Events.
|
|
**
|
|
** Returns: Number of lines that should be skipped before scanning
|
|
** for another event starts again.
|
|
*/
|
|
DWORD
|
|
HandleBeginProcSetQuery(
|
|
PJR pjr,
|
|
PSZ dummy
|
|
)
|
|
{
|
|
DICT_RECORD QDict;
|
|
PQR pqr = pjr->job_pQr;
|
|
DWORD rc;
|
|
|
|
DBGPRINT(("Enter HandleBeginProcSetQuery\n"));
|
|
|
|
//
|
|
// the dictionary the job is looking for determines what
|
|
// client version the job originated from.
|
|
//
|
|
FindDictVer(&QDict);
|
|
|
|
//
|
|
// if we are a 5.2 client, then reset this to be a PSTODIB job
|
|
//
|
|
if ((_stricmp(QDict.name, MDNAME) == 0) &&
|
|
(_stricmp(QDict.version, CHOOSER_52) == 0))
|
|
{
|
|
DBGPRINT(("a 5.2 client - we do not support him\n"));
|
|
rc = ERROR_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
// we don't cache any other dictionaries, so tell client we
|
|
// don't have it
|
|
rc = TellClient(pjr,
|
|
pjr->EOFRecvd,
|
|
PROCSETMISSINGRESPONSE,
|
|
strlen(PROCSETMISSINGRESPONSE));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
**
|
|
** HandleBeginFontQuery()
|
|
**
|
|
** Purpose: Handles BeginFontQuery Comment Events.
|
|
**
|
|
** Returns: PAPWrite Error Codes
|
|
**
|
|
*/
|
|
DWORD
|
|
HandleBeginFontQuery(
|
|
PJR pjr,
|
|
PSZ ps
|
|
)
|
|
{
|
|
PQR pqr = pjr->job_pQr;
|
|
CHAR response[PSLEN + 3];
|
|
LPSTR pszResponseFont = response;
|
|
DWORD cbResponseUsed = 0;
|
|
LPSTR requestedFont = NULL;
|
|
DWORD len= 0;
|
|
DWORD rc = NO_ERROR;
|
|
|
|
DBGPRINT(("Enter HandleBeginFontQuery\n"));
|
|
|
|
do
|
|
{
|
|
// parse out the fontname list
|
|
requestedFont= strtok(NULL,"\n");
|
|
|
|
if (NULL == requestedFont)
|
|
{
|
|
rc = (DWORD)-1; // Special error code to indicate we want default handling
|
|
break;
|
|
}
|
|
|
|
len = strlen(requestedFont);
|
|
|
|
DBGPRINT(("requesting font list:%s. Length: %d\n", requestedFont, len));
|
|
|
|
// Mac will request status on a list of fonts separated by spaces.
|
|
// for each font we respond with /fontname:yes or /fontname:no and
|
|
// bundle this response into one write
|
|
requestedFont = strtok(requestedFont, " ");
|
|
while (requestedFont != NULL)
|
|
{
|
|
DBGPRINT(("looking for font:%s\n", requestedFont));
|
|
|
|
// enough space for response?
|
|
if (PSLEN < (cbResponseUsed + strlen(requestedFont) + sizeof(":yes ")))
|
|
{
|
|
DBGPRINT(("out of space for response\n"));
|
|
break;
|
|
}
|
|
|
|
if (IsFontAvailable(pqr, requestedFont))
|
|
{
|
|
sprintf(pszResponseFont, "/%s:Yes\x0a", requestedFont);
|
|
}
|
|
else
|
|
{
|
|
sprintf(pszResponseFont, "/%s:No\x0a", requestedFont);
|
|
}
|
|
|
|
cbResponseUsed += strlen(pszResponseFont);
|
|
pszResponseFont += strlen(pszResponseFont);
|
|
requestedFont = strtok(NULL, " ");
|
|
}
|
|
} while (FALSE);
|
|
|
|
strcpy (pszResponseFont, "*\x0a");
|
|
|
|
if (NO_ERROR == rc)
|
|
{
|
|
DBGPRINT(("responding with:%s", response));
|
|
rc = TellClient(pjr, pjr->EOFRecvd, response, strlen(response));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
IsFontAvailable(
|
|
PQR pqr,
|
|
LPSTR pszFontName
|
|
)
|
|
{
|
|
BOOL rc = FALSE;
|
|
DWORD i;
|
|
PFR fontPtr;
|
|
HANDLE hFontQuery = INVALID_HANDLE_VALUE;
|
|
DWORD cFonts;
|
|
DWORD dummy;
|
|
CHAR pszFont[PPDLEN + 1];
|
|
DWORD cbFont = 0;
|
|
DWORD err;
|
|
|
|
DBGPRINT(("enter IsFontAvailable\n"));
|
|
|
|
do
|
|
{
|
|
//
|
|
// fonts for Postscript queues different than for PSTODIB queues
|
|
//
|
|
|
|
if (!wcscmp(pqr->pDataType, MACPS_DATATYPE_RAW))
|
|
{
|
|
//
|
|
// do a PostScript queue font search
|
|
//
|
|
|
|
DBGPRINT(("starting font search on PostScript queue\n"));
|
|
|
|
for (i = 0, fontPtr = pqr->fonts; i <= pqr->MaxFontIndex; i++, fontPtr++)
|
|
{
|
|
if (!_stricmp(pszFontName, fontPtr->name))
|
|
{
|
|
DBGPRINT(("found the font\n"));
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// do a PSTODIB font search
|
|
//
|
|
DBGPRINT(("starting font search on PSTODIB queue\n"));
|
|
|
|
if (PS_QFONT_SUCCESS != (PsBeginFontQuery(&hFontQuery)))
|
|
{
|
|
DBGPRINT(("PsBeginFontQuery fails\n"));
|
|
hFontQuery = INVALID_HANDLE_VALUE;
|
|
break;
|
|
}
|
|
|
|
if (PS_QFONT_SUCCESS != (PsGetNumFontsAvailable(hFontQuery, &cFonts)))
|
|
{
|
|
DBGPRINT(("psGetNumFontsAvailable fails\n"));
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < cFonts; i++)
|
|
{
|
|
cbFont = PPDLEN + 1;
|
|
dummy = 0;
|
|
err = PsGetFontInfo(hFontQuery, i, pszFont, &cbFont, NULL, &dummy);
|
|
if (PS_QFONT_SUCCESS != err)
|
|
{
|
|
DBGPRINT(("PsGetFontInfo fails with %d\n", err));
|
|
break;
|
|
}
|
|
|
|
if (0 == _stricmp(pszFontName, pszFont))
|
|
{
|
|
DBGPRINT(("found the font\n"));
|
|
rc = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} while (FALSE);
|
|
|
|
if (INVALID_HANDLE_VALUE != hFontQuery)
|
|
{
|
|
PsEndFontQuery(hFontQuery);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
**
|
|
** HandleEndPrinterQuery()
|
|
**
|
|
** Purpose: Handles EndPrinterQuery Comment Events.
|
|
**
|
|
*/
|
|
DWORD
|
|
HandleEndPrinterQuery(
|
|
PJR pjr
|
|
)
|
|
{
|
|
char reply[PSLEN+1];
|
|
PQR QPtr = pjr->job_pQr;
|
|
|
|
DBGPRINT(("Enter HandleEndPrinterQuery\n"));
|
|
|
|
/* respond with revision number, version and product */
|
|
sprintf(reply, "%s\n(%s)\n(%s)\n", QPtr->Revision, QPtr->Version, QPtr->Product);
|
|
|
|
/* respond to the client */
|
|
return (TellClient(pjr, pjr->EOFRecvd, reply, strlen(reply)));
|
|
}
|
|
|
|
|
|
/*
|
|
** HandleBeginXQuery()
|
|
**
|
|
** Purpose: Handles BeginQuery Comment Events.
|
|
*/
|
|
void
|
|
HandleBeginXQuery(
|
|
PJR pjr,
|
|
PSZ string
|
|
)
|
|
{
|
|
DBGPRINT(("BeginQuery: %s\n", string));
|
|
strcpy(pjr->JSKeyWord, string);
|
|
pjr->InProgress=QUERYDEFAULT;
|
|
}
|