windows-nt/Source/XPSP1/NT/admin/netui/macprint/spooler/psp.c

1570 lines
31 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
////////////////////////////////////////////////////////////////////////////////
//
// MacPrint - Windows NT Print Server for Macintosh Clients
// Copyright (c) Microsoft Corp., 1991, 1992, 1993
//
// psp.c - Macintosh Print Service Postscript Parsing Routines
//
// Author: Frank D. Byrum
// adapted from MacPrint from LAN Manager Services for Macintosh
//
// DESCRIPTION:
// This module provides the routines to parse the Adobe DSC 2.0
// comments in a PostScript stream.
//
////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include <macpsmsg.h>
#include <macps.h>
#include <pskey.h>
#include <debug.h>
// function prototypes
DWORD HandleTitle(PJR pjr);
DWORD HandleBeginExitServer(PJR pjr);
DWORD HandleCreationDate(PJR pjr);
DWORD HandleCreator(PJR pjr);
DWORD HandleEndExitServer(PJR pjr);
DWORD HandleEOF(PJR pjr);
DWORD HandleFor(PJR pjr);
DWORD HandleLogin(PJR pjr);
DWORD HandleBeginProcSet(PJR pjr);
DWORD HandleEndProcSet(PJR pjr);
DWORD HandleIncludeProcSet(PJR pjr);
DWORD HandleComment(PJR, PBYTE);
DWORD HandleBeginBinary(PJR pjr);
DWORD HandleEndBinary(PJR pjr);
DWORD HandlePages(PJR pjr);
void HandleJobComment (PJR, PBYTE);
PFR ReAllocateFontList (PFR pfrOld, DWORD cOldFonts, DWORD cNewFonts);
#if DBG_SPOOL_LOCALLY
HANDLE DbgSpoolFile = INVALID_HANDLE_VALUE;
#endif
char * deffonts[DEFAULTFONTS] =
{
FONT00, FONT01, FONT02, FONT03, FONT04, FONT05, FONT06, FONT07,
FONT08, FONT09, FONT10, FONT11, FONT12, FONT13, FONT14, FONT15,
FONT16, FONT17, FONT18, FONT19, FONT20, FONT21, FONT22, FONT23,
FONT24, FONT25, FONT26, FONT27, FONT28, FONT29, FONT30, FONT31,
FONT32, FONT33, FONT34
};
////////////////////////////////////////////////////////////////////////////////
//
// SetDefaultPPDInfo() - Initialize to LaserWriter Plus configuration
//
// DESCRIPTION:
// This routine is used to set the default parameters of our
// printer to LaserWriter Plus characteristics. This is used
// in the event there is no PPD file associated with the given
// NT Printer Object (as in the case of non Postscript printers)
//
// returns true if queue structure initialized OK.
//
////////////////////////////////////////////////////////////////////////////////
BOOLEAN
SetDefaultPPDInfo(
PQR pqr
)
{
DWORD i;
//
// initialize Postscript keywords
//
strcpy(pqr->LanguageVersion, ENGLISH);
strcpy(pqr->Product, DEFAULTPRODUCTRESPONSE);
strcpy(pqr->Version, DEFAULTPSVERSION);
strcpy(pqr->Revision, DEFAULTPSREVISION);
strcpy(pqr->DeviceNickName, UNKNOWNPRINTER);
strcpy(pqr->pszColorDevice, COLORDEVICEDEFAULT);
strcpy(pqr->pszResolution, RESOLUTIONDEFAULT);
strcpy(pqr->pszLanguageLevel, DEFAULTLANGUAGELEVEL);
pqr->FreeVM = VMDEFAULT;
pqr->SupportsBinary = FALSE;
pqr->fonts = NULL;
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////////
//
// SetDefaultFonts() - Initialize to LaserWriter Plus configuration
//
// DESCRIPTION:
// This routine is used to set the default parameters of our
// printer to LaserWriter Plus characteristics. This is used
// in the event there is no PPD file associated with the given
// NT Printer Object (as in the case of non Postscript printers)
//
// returns true if queue structure initialized OK.
//
////////////////////////////////////////////////////////////////////////////////
BOOLEAN
SetDefaultFonts(
PQR pqr
)
{
DWORD i;
if (pqr->fonts != NULL)
{
DBGPRINT(("ERROR: pqr->fonts is nonnull!\n"));
}
pqr->fonts = (PFR)LocalAlloc(LPTR, DEFAULTFONTS * sizeof (FONT_RECORD));
if (pqr->fonts == NULL)
{
DBGPRINT(("ERROR: unable to allocate font data\n"));
ReportEvent(
hEventLog,
EVENTLOG_ERROR_TYPE,
EVENT_CATEGORY_INTERNAL,
EVENT_SERVICE_OUT_OF_MEMORY,
NULL, 0, 0, NULL, NULL);
return (FALSE);
}
//
// copy font names
//
for (i = 0; i < DEFAULTFONTS; i++)
{
strcpy(pqr->fonts[i].name, deffonts[i]);
}
pqr->MaxFontIndex = DEFAULTFONTS-1;
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////////
//
// GetPPDInfo() - Initialize to LaserWriter Plus configuration
//
// DESCRIPTION:
// This routine is used to set the parameters of our
// printer to the characteristics specified in the PPD
// file for the printer.
//
// returns true if queue structure initialized OK.
//
////////////////////////////////////////////////////////////////////////////////
BOOLEAN
GetPPDInfo(
PQR pqr
)
{
FILE * ppdfile = NULL;
char * result = NULL;
char * token = NULL;
char line[PSLEN];
PFR fontPtr=NULL;
USHORT MaxFonts = 100;
USHORT fontindex = 0;
LPDRIVER_INFO_2 pdiThis = NULL;
DWORD cbpdiThis = sizeof(DRIVER_INFO_2) + 256;
LPSTR pszPPDFile = NULL;
BOOLEAN ReturnStatus = TRUE;
HANDLE hPrinter = INVALID_HANDLE_VALUE;
int toklen;
do
{
// get the path of the ppdfile
if (!OpenPrinter(pqr->pPrinterName, &hPrinter, NULL))
{
hPrinter = INVALID_HANDLE_VALUE;
DBGPRINT(("ERROR: unable to get printer handle, error=%d\n", GetLastError()));
ReturnStatus = FALSE;
break;
}
pdiThis = (LPDRIVER_INFO_2) LocalAlloc(LPTR, cbpdiThis);
if (pdiThis == NULL)
{
DBGPRINT(("ERROR: unable to allocate new driverinfo buffer\n"));
ReturnStatus = FALSE;
break;
}
if (!GetPrinterDriver(hPrinter,
NULL,
2,
(LPBYTE) pdiThis,
cbpdiThis,
&cbpdiThis))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
DBGPRINT(("ERROR: unable to get printer driver info\n"));
ReturnStatus = FALSE;
break;
}
LocalFree(pdiThis);
pdiThis = (LPDRIVER_INFO_2) LocalAlloc(LPTR, cbpdiThis);
if (pdiThis == NULL)
{
DBGPRINT(("ERROR: unable to allocte new driverinfo buffer\n"));
ReturnStatus = FALSE;
break;
}
if (!GetPrinterDriver(hPrinter,
NULL,
2,
(LPBYTE) pdiThis,
cbpdiThis,
&cbpdiThis))
{
DBGPRINT(("ERROR: unable to get printer driver info\n"));
ReturnStatus = FALSE;
break;
}
}
#ifdef DBCS
pszPPDFile = (LPSTR)LocalAlloc(LPTR, (wcslen(pdiThis->pDataFile)+1) * sizeof(WCHAR));
#else
pszPPDFile = (LPSTR)LocalAlloc(LPTR, wcslen(pdiThis->pDataFile)+1);
#endif
DBGPRINT(("pDataFile name length = %d\n", wcslen(pdiThis->pDataFile)));
if (pszPPDFile == NULL)
{
DBGPRINT(("out of memory for pszPPDFile\n"));
ReturnStatus = FALSE;
break;
}
CharToOem(pdiThis->pDataFile, pszPPDFile);
DBGPRINT(("pDataFile = %ws, pszPPDFile = %s\n", pdiThis->pDataFile, pszPPDFile));
if ((ppdfile = fopen(pszPPDFile, "rt")) == NULL)
{
DBGPRINT(("File open error %s", pszPPDFile));
ReturnStatus = FALSE;
break;
}
/*
* Allocate a buffer for fonts. We don't know yet what size we need.
* We make a guess and increase the size as we go. The incremental
* size is 10 fonts. We start off with 100. We shrink the segment size
* to the final size.
*/
fontPtr = (PFR) LocalAlloc (LPTR, sizeof(FONT_RECORD)*MaxFonts);
if (fontPtr == NULL)
{
DBGPRINT(("ERROR: cannot allocate font list buffer, error=%d\n", GetLastError()));
ReturnStatus = FALSE;
break;
}
pqr->SupportsBinary = FALSE; // Default
while (result = fgets(line, PSLEN, ppdfile))
{
if (line[0] != ASTERISK || (token= strtok(line, " \011")) == NULL)
continue;
// PPD Font Entry?
if (!_stricmp(line, ppdFONT))
{
/* This should be the fontname */
if ((token= strtok(NULL, " \011:")) != NULL)
{
if (strlen(token) <= FONTNAMELEN)
{
strcpy(fontPtr[fontindex].name, token);
DBGPRINT(("Font: %s\n", token));
fontindex++;
if (fontindex >= MaxFonts)
{
fontPtr = ReAllocateFontList (fontPtr, MaxFonts, MaxFonts + 10);
if (fontPtr == NULL)
{
DBGPRINT(("ERROR: unable to grow font buffer, error=%d\n", GetLastError()));
ReturnStatus = FALSE;
break;
}
MaxFonts += 10;
}
}
else DBGPRINT(("Fontname > PPDLEN ???\n"));
}
}
else if (!_stricmp(token, ppdPSVERSION))
{
// PPD Postscript Version Entry?
/* Get the PostScript version */
token= strtok(NULL, "\011()\""); /* This should be the version */
if (token != NULL)
{
toklen = strlen(token);
/* Get the PostScript revision */
if ((toklen <= PPDLEN) && (toklen > 0))
{
strcpy(pqr->Version, token);
DBGPRINT(("Version: %s\n", pqr->Version));
}
else
{
strcpy(pqr->Version, "1.0"); // Default
DBGPRINT(("Version > PPDLEN ???\n"));
}
token= strtok(NULL, "()\""); /* This should be the revision */
if (token != NULL)
{
while ((*token != '\0') && (*token == ' '))
token ++;
toklen = strlen(token);
if ((toklen <= PPDLEN) && (toklen > 0))
{
strcpy(pqr->Revision, token);
DBGPRINT(("Revision: %s\n", pqr->Revision));
}
else
{
strcpy(pqr->Revision, "1.0"); // Some bogus token
DBGPRINT(("Revision > PPDLEN ???\n"));
}
}
else
{
strcpy(pqr->Version, "1.0"); // Defaults
strcpy(pqr->Revision, "1.0");
}
}
}
else if (!_stricmp(token, ppdNICKNAME))
{
// PPD NickName?
/* Get the NICKNAME */
token= strtok(NULL, "\011()\""); /* This should be the nickname */
if ((token != NULL) && (strlen(token) <= PPDLEN))
{
strcpy(pqr->DeviceNickName, token);
DBGPRINT(("DeviceNickName: %s\n", pqr->DeviceNickName));
}
else DBGPRINT(("DeviceNickName > PPDLEN ???\n"));
}
else if (!_stricmp(token, ppdLANGUAGEVERSION))
{
// PPD Postscript Language Version?
/* Get the LANGUAGEVERSION */
token= strtok(NULL, " \011:"); /* This should be the language */
if ((token != NULL) && (strlen(token) <= PPDLEN))
{
strcpy(pqr->LanguageVersion, token);
DBGPRINT(("LanguageVersion: %s\n", pqr->LanguageVersion));
}
else DBGPRINT(("LanguageVersion > PPDLEN ???\n"));
}
else if (!_stricmp(token, ppdPRODUCT))
{
// PPD Product ?
/* Get the PRODUCT */
token = strtok(NULL, "\011()\""); /* This should be the product */
if ((token != NULL) && (strlen(token) <= PPDLEN))
{
strcpy(pqr->Product, token);
DBGPRINT(("Product: %s\n", pqr->Product));
}
else DBGPRINT(("Product > PPDLEN ???\n"));
}
else if (!_stricmp(token, ppdFREEVM))
{
token= strtok(NULL, "\011()\""); /* This should be the product */
if (token != NULL)
sscanf(token, "%ld", &pqr->FreeVM);
DBGPRINT(("Free VM: %ld\n", pqr->FreeVM));
}
else if (!_stricmp(token, ppdCOLORDEVICE))
{
// this should be a string indicating color support or not
// in the form of <True> or <False> (brackets not included)
token = strtok(NULL, " \011:\x0d\x0a");
if ((token != NULL) && (strlen(token) < COLORDEVICEBUFFLEN))
{
strcpy (pqr->pszColorDevice, token);
}
else
{
strcpy (pqr->pszColorDevice, COLORDEVICEDEFAULT);
}
DBGPRINT(("Color device: %s\n", pqr->pszColorDevice));
}
else if (!_stricmp(token, ppdDEFAULTRESOLUTION))
{
// this should be a string indicating the default
// resolution of the printer in the form <xxxxdpi>
// where xxxx is a number
token = strtok(NULL, " \011:\x0d\x0a");
if ((token != NULL) && (strlen(token) < RESOLUTIONBUFFLEN))
{
strcpy (pqr->pszResolution, token);
}
else
{
strcpy (pqr->pszResolution, RESOLUTIONDEFAULT);
}
DBGPRINT(("Resolution: %s\n", pqr->pszResolution));
}
else if (!_stricmp(token, ppdLANGUAGELEVEL))
{
// this should be the PostScript level ("1" or "2")
// implemented in this printer
token = strtok(NULL, " \011\"");
if ((token != NULL) && (PPDLEN >= strlen(token)))
{
strcpy (pqr->pszLanguageLevel, token);
}
else
{
strcpy (pqr->pszLanguageLevel, DEFAULTLANGUAGELEVEL);
}
DBGPRINT(("Language Level: %s\n", pqr->pszLanguageLevel));
}
else if (!_stricmp(line, ppdPROTOCOL))
{
/* Get the string following and see if it is BCP or TBCP ? */
if ((token= strtok(NULL, " \011:")) != NULL)
{
if (strstr(token, PROTOCOL_BCP) != NULL)
{
pqr->SupportsBinary = TRUE;
}
}
}
}
if (!ReturnStatus)
{
pqr->fonts = NULL;
pqr->MaxFontIndex = 0;
}
else
{
pqr->fonts = fontPtr;
pqr->MaxFontIndex = fontindex-1;
}
} while (FALSE);
if (pszPPDFile != NULL)
{
LocalFree(pszPPDFile);
}
if (ppdfile != NULL)
{
fclose(ppdfile);
}
if (hPrinter != INVALID_HANDLE_VALUE)
{
ClosePrinter(hPrinter);
}
if (pdiThis != NULL)
{
LocalFree(pdiThis);
}
if (!ReturnStatus)
{
if (fontPtr != NULL)
{
LocalFree(fontPtr);
}
}
return (ReturnStatus);
}
PFR
ReAllocateFontList(
PFR pfrOld,
DWORD cOldFonts,
DWORD cNewFonts
)
{
PFR pfrNew = NULL;
DBGPRINT(("enter ReAllocateFontList()\n"));
do
{
// allocate new font record
pfrNew = LocalAlloc(LPTR, cNewFonts * sizeof(FONT_RECORD));
if (pfrNew == NULL)
{
DBGPRINT(("LocalAlloc fails with %d\n", GetLastError()));
break;
}
//
// copy old font record
//
CopyMemory(pfrNew, pfrOld, cOldFonts * sizeof(FONT_RECORD));
} while (FALSE);
LocalFree(pfrOld);
return pfrNew;
}
/*
**
** WriteToSpool()
**
** Purpose: Determines if job stream is currently being written to
** the spooler, then writes it to the file if it is being written.
**
** Returns: fwrite return codes.
**
*/
DWORD
WriteToSpool(
PJR pjr,
PBYTE pchbuf,
int cchlen
)
{
BOOL SpoolIt=FALSE;
DWORD cbWritten;
DWORD dwError = NO_ERROR;
if ((cchlen !=0) && (pchbuf != NULL) &&
((pjr->psJobState==psExitServerJob) || (pjr->psJobState==psStandardJob)))
{
/* determine the data stream mode to know whether to write */
switch (pjr->JSState)
{
case JSStripEOL:
case JSStripKW:
case JSStripTok:
DBGPRINT(("POP - strip\n"));
PopJSState(pjr);
break;
case JSWriteEOL:
case JSWriteKW:
case JSWriteTok:
DBGPRINT(("POP - write\n"));
PopJSState(pjr);
case JSWrite:
SpoolIt=TRUE;
break;
}
// Do we write this Data to the Output Stream ?
if (SpoolIt)
{
// retry on disk full conditions.
LONG RetryCount = 0;
do
{
dwError = NO_ERROR;
do
{
if (pjr->FirstWrite)
{
// don't need that filter string anymore
#if 0
//
// place comment in job to signal AppleTalk monitor not to filter control characters
//
if (!WritePrinter(pjr->hPrinter, FILTERCONTROL, SIZE_FC, &cbWritten))
{
dwError = GetLastError();
DBGPRINT(("WritePrinter() failed with %d\n", dwError));
RetryCount++;
break;
}
#endif
pjr->FirstWrite = FALSE;
}
#if DBG_SPOOL_LOCALLY
if (DbgSpoolFile != INVALID_HANDLE_VALUE)
{
WriteFile( DbgSpoolFile, pchbuf, cchlen, &cbWritten, NULL );
}
#endif
if (!WritePrinter(pjr->hPrinter, pchbuf, cchlen, &cbWritten))
{
dwError = GetLastError();
DBGPRINT(("ERROR: cannot write to printer, error = %x\n", dwError));
RetryCount++;
break;
}
} while (FALSE);
if (dwError == NO_ERROR)
break;
if ((dwError == ERROR_HANDLE_DISK_FULL) || (dwError == ERROR_DISK_FULL))
{
Sleep(180*1000); // 3 minutes. Its okay to block since we cannot
// service any other jobs either since the disk
// has no space anyway
}
} while (RetryCount <= 10);
}
}
return dwError;
}
/*
** MoveToPending()
**
** Purpose: Moves the buffer pointed at into the pending buffer.
**
** Returns: DosWrite error codes.
**
*/
DWORD
MoveToPending(
PJR pjr,
PBYTE pchbuf,
int cchlen
)
{
DBGPRINT(("Enter MoveToPending\n"));
if ((cchlen > PSLEN) || (*pchbuf != '%'))
{
/*
* input line is not a comment and is conforming PostScript line,
* so give it to WriteToSpool
*/
DBGPRINT(("not a DSC comment, so sending to spooler\n"));
return (WriteToSpool (pjr, pchbuf, cchlen));
}
pjr->PendingLen= cchlen;
memcpy(&pjr->bufPool[pjr->bufIndx].PendingBuffer[PENDLEN-cchlen], pchbuf, cchlen);
return (NO_ERROR);
}
/*
** TellClient ()
**
** Purpose: Sends a message back to the client
**
** Returns: Any of the PAPWrite return codes.
**
*/
DWORD
TellClient(
PJR pjr,
BOOL fEof,
PBYTE BuffPtr,
int cchlen
)
{
DWORD rc = NO_ERROR;
fd_set writefds;
struct timeval timeout;
int sendflag;
int wsErr;
DBGPRINT(("enter TellClient()\n"));
do
{
FD_ZERO(&writefds);
FD_SET(pjr->sJob, &writefds);
//
// wait up to 30 seconds to be able to write
//
if (fEof)
{
sendflag = 0;
}
else
{
sendflag = MSG_PARTIAL;
}
timeout.tv_sec = 30;
timeout.tv_usec = 0;
DBGPRINT(("waiting for writeability\n"));
wsErr = select(0, NULL, &writefds, NULL, &timeout);
if (wsErr == 0)
{
DBGPRINT(("response to client times out\n"));
rc = ERROR_SEM_TIMEOUT;
break;
}
if (wsErr != 1)
{
rc = GetLastError();
DBGPRINT(("select(writefds) fails with %d\n"));
break;
}
if (send(pjr->sJob, BuffPtr, cchlen, sendflag) == SOCKET_ERROR)
{
rc = GetLastError();
DBGPRINT(("send() fails with %d\n", rc));
break;
}
} while (FALSE);
return rc;
}
/*
**
** HandleBeginBinary()
**
** Purpose: Handles BeginBinary Comment Events.
**
*/
DWORD
HandleBeginBinary(
PJR pjr
)
{
DBGPRINT(("Enter HandleBeginBinary\n"));
/* Process the BeginBinary Comment */
pjr->InBinaryOp = TRUE;
return NO_ERROR;
}
/*
**
** HandleEndBinary()
**
** Purpose: Handles BeginBinary Comment Events.
**
*/
DWORD
HandleEndBinary(
PJR pjr
)
{
DBGPRINT(("Enter HandleEndBinary\n"));
// Process the EndBinary Comment
pjr->InBinaryOp = FALSE;
return NO_ERROR;
}
/*
**
** HandleBeginExitServer()
**
** Purpose: Handles BeginExitServer Comment Events.
**
*/
DWORD
HandleBeginExitServer(
PJR pjr
)
{
DBGPRINT(("Enter HandleBeginExitServer\n"));
switch (pjr->psJobState)
{
case psQueryJob:
case psExitServerJob:
PushJSState(pjr, JSStrip);
break;
case psStandardJob:
PushJSState(pjr, JSStripEOL);
break;
}
return NO_ERROR;
}
/*
**
** HandleCreationDate()
**
** Purpose: Handles CreationDate Comment Events.
**
** Returns: Number of lines that should be skipped before scanning
** for another event starts again.
**
*/
DWORD
HandleCreationDate(
PJR pjr
)
{
return NO_ERROR;
}
/*
**
** HandleCreator() -
**
** Purpose: Handles Creator Comment Events.
**
*/
DWORD
HandleCreator(
PJR pjr
)
{
return NO_ERROR;
}
/*
**
** HandleEndExitServer()-
**
** Purpose: Handles EndExitServer Comment Events.
**
*/
DWORD
HandleEndExitServer(
PJR pjr
)
{
DBGPRINT(("Enter HandleEndExitServer\n"));
if (pjr->psJobState == psStandardJob)
PushJSState (pjr, JSStripEOL);
return NO_ERROR;
}
/*
** HandleEOF()
**
** Purpose: Handles EOF Comment Events.
**
*/
DWORD
HandleEOF(
PJR pjr
)
{
DBGPRINT(("Enter HandleEOF\n"));
if (pjr->psJobState == psQueryJob || pjr->psJobState == psExitServerJob)
{
pjr->psJobState = psStandardJob;
}
// pjr->JSState = JSStripKW;
return NO_ERROR;
}
/*
**
** HandleFor()
**
** Purpose: Handles For Comment Events.
**
*/
DWORD
HandleFor(
PJR pjr
)
{
LPSTR token;
BYTE pbBuffer[GENERIC_BUFFER_SIZE];
PJOB_INFO_1 pji1Job;
DWORD cbNeeded;
DWORD Status = NO_ERROR;
DBGPRINT(("Enter HandleFor\n"));
//
// only look for name in main part of print job
//
if (pjr->psJobState != psStandardJob)
{
DBGPRINT(("not in standard job, skipping username\n"));
return NO_ERROR;
}
//
// make sure we haven't already set the title
//
if (pjr->dwFlags & JOB_FLAG_OWNERSET)
{
DBGPRINT(("owner already set, skipping username\n"));
return NO_ERROR;
}
//
// mark the job as having an owner
//
pjr->dwFlags |= JOB_FLAG_OWNERSET;
//
// look for the client name in the comment and
// default if not found
//
if (((token = strtok(NULL, NULL_STR)) == NULL) ||
(strchr(token, '*') != NULL))
{
token = CLIENTNAME;
}
//
// get the current job info
//
pji1Job = (PJOB_INFO_1)pbBuffer;
if (!GetJob(pjr->hPrinter,
pjr->dwJobId,
1,
pbBuffer,
GENERIC_BUFFER_SIZE,
&cbNeeded))
{
//
// need more buffer? If so, try again with a larger one
//
if (cbNeeded > GENERIC_BUFFER_SIZE)
{
DBGPRINT(("GetJob needs larger buffer. Retrying\n"));
pji1Job = (PJOB_INFO_1)LocalAlloc(LPTR, cbNeeded);
if (pji1Job == NULL)
{
Status = GetLastError();
DBGPRINT(("ERROR: out of memory in HandleFor\n"));
return Status;
}
if (!GetJob(pjr->hPrinter,
pjr->dwJobId,
1,
(LPBYTE)pji1Job,
cbNeeded,
&cbNeeded))
{
Status = GetLastError();
DBGPRINT(("ERROR: second GetJob fails in HandleFor with %d\n",
Status));
return Status;
}
}
else
{
Status = GetLastError();
DBGPRINT(("GetJob fails with %d\n", Status));
return Status ;
}
}
//
// change the username
//
OemToChar(token, pjr->pszUser);
pji1Job->pUserName = pjr->pszUser;
DBGPRINT(("Setting user name to %ws\n", pjr->pszUser));
//
// set new job information (do not change job position)
//
pji1Job->Position = 0;
if (!SetJob(pjr->hPrinter,
pjr->dwJobId,
1,
(LPBYTE)pji1Job,
0))
{
Status = GetLastError();
DBGPRINT(("WARNING: tried to change user name and failed setjob with %d\n", Status));
}
return Status;
}
/*
**
** HandleLogin()
**
** Purpose: Handles Login Comment Events.
**
** Returns: PAPWrite errors.
**
*/
DWORD
HandleLogin(
PJR pjr
)
{
DBGPRINT(("Enter HandleLogin\n"));
PushJSState(pjr,JSStripEOL);
return (TellClient(pjr, TRUE, LOGINRESPONSE, sizeof(LOGINRESPONSE)-1));
}
/*
**
** HandleTitle()
**
** Purpose: Handles Title Comment Events.
**
*/
DWORD
HandleTitle(
PJR pjr
)
{
LPSTR token;
LPWSTR pszTitle;
BYTE pbBuffer[GENERIC_BUFFER_SIZE];
PJOB_INFO_1 pji1Job;
PJOB_INFO_1 pji1JobAlloc=NULL;
DWORD cbNeeded;
DWORD Status = NO_ERROR;
DBGPRINT(("Enter HandleTitle\n"));
//
// only get title if we are in main part of job
//
if (pjr->psJobState != psStandardJob)
{
DBGPRINT(("skipping this title, not main job\n"));
return NO_ERROR ;
}
//
// make sure title not already set
//
if (JOB_FLAG_TITLESET & pjr->dwFlags)
{
DBGPRINT(("title already set. Skipping this title\n"));
return NO_ERROR;
}
//
// marke the title as set
//
pjr->dwFlags |= JOB_FLAG_TITLESET;
//
// get the current job data
//
pji1Job = (PJOB_INFO_1)pbBuffer;
if (!GetJob(pjr->hPrinter,
pjr->dwJobId,
1,
pbBuffer,
GENERIC_BUFFER_SIZE,
&cbNeeded))
{
//
// need more buffer? If so, try again with a larger one
//
if (cbNeeded > GENERIC_BUFFER_SIZE)
{
DBGPRINT(("GetJob needs larger buffer. Retrying\n"));
pji1JobAlloc = (PJOB_INFO_1)LocalAlloc(LPTR, cbNeeded);
if (pji1JobAlloc == NULL)
{
Status = GetLastError();
DBGPRINT(("ERROR: out of memory\n"));
return Status;
}
pji1Job = pji1JobAlloc;
if (!GetJob(pjr->hPrinter,
pjr->dwJobId,
1,
(LPBYTE)pji1Job,
cbNeeded,
&cbNeeded))
{
Status = GetLastError();
DBGPRINT(("ERROR: second GetJob fails with %d\n", Status));
LocalFree(pji1JobAlloc);
return Status;
}
}
else
{
Status = GetLastError();
DBGPRINT(("GetJob fails with %d\n", Status));
return Status;
}
}
//
// get the title
//
if ((token = strtok(NULL, NULL_STR)) == NULL)
{
// Clear flag. No title.
pjr->dwFlags &= ~JOB_FLAG_TITLESET;
return NO_ERROR ;
}
pszTitle = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (strlen(token)+1));
if (pszTitle == NULL)
{
Status = GetLastError();
DBGPRINT(("out of memory for pszTitle\n"));
return Status;
}
OemToChar(token, pszTitle);
//
// change the title
//
pji1Job->Position = 0;
pji1Job->pDocument = pszTitle;
DBGPRINT(("changing title to %ws\n", pszTitle));
if (!SetJob(pjr->hPrinter,
pjr->dwJobId,
1,
(LPBYTE)pji1Job,
0))
{
Status = GetLastError();
DBGPRINT(("WARNING: tried to change title and failed setjob with %d\n", Status));
}
if (pji1JobAlloc)
{
LocalFree(pji1JobAlloc);
}
LocalFree(pszTitle);
return Status;
}
/*
**
** HandleBeginProcSet()
**
** Purpose: Handles Begining of a ProcSet Upload
**
*/
DWORD
HandleBeginProcSet(
PJR pjr
)
{
DBGPRINT(("Enter HandleBeginProcSet\n"));
return NO_ERROR;
}
/*
** HandleEndProcSet()
**
** Purpose: Handles End of a procset inclusion.
**
*/
DWORD
HandleEndProcSet(
PJR pjr
)
{
DBGPRINT(("Enter HandleEndProcSet\n"));
return NO_ERROR;
}
/*
** HandleIncludeProcSet()
**
** Purpose: Handles end of a procset inclusion.
**
** Entry:
** Pointer to Job Structure
**
** Exit:
**
** 0 if no error, otherwise error code.
*/
DWORD
HandleIncludeProcSet(
PJR pjr
)
{
DBGPRINT(("Enter HandleIncludeProcSet\n"));
return NO_ERROR;
}
///////////////////////////////////////////////////////////////////////////////
//
// HandlePages()
//
// This comment includes the total number of pages in the job and is
// used to set the jobinfo structure for the job with the total number
// of pages
//
///////////////////////////////////////////////////////////////////////////////
DWORD
HandlePages(
PJR pjr
)
{
LPSTR token;
DWORD cPages = 0;
BYTE pbBuffer[GENERIC_BUFFER_SIZE];
PJOB_INFO_1 pji1Job;
DWORD cbNeeded;
DWORD Status = NO_ERROR;
DBGPRINT(("Enter HandlePages\n"));
//
// only get pages if we are in main part of job
//
if (pjr->psJobState != psStandardJob)
{
DBGPRINT(("skipping this comment, not main job\n"));
return NO_ERROR ;
}
//
// get the current job data
//
pji1Job = (PJOB_INFO_1)pbBuffer;
if (!GetJob(pjr->hPrinter,
pjr->dwJobId,
1,
pbBuffer,
GENERIC_BUFFER_SIZE,
&cbNeeded))
{
//
// GetJob failed, and buffer passed in is larger than the largest
// possible buffer for a job_info_1, so abort this ADSC comment
//
Status = GetLastError();
DBGPRINT(("GetJob() fails with %d\n", Status));
return Status;
}
//
// get the number of pages. The comment is of the form %%Pages xx nn
// where xx is the number of pages to display
//
token = strtok(NULL, " ");
if (token == NULL)
return(NO_ERROR);
cPages = atoi(token);
//
// change the number of pages
//
pji1Job->Position = 0;
pji1Job->TotalPages = cPages;
DBGPRINT(("changing page count to %d\n", cPages));
if (!SetJob(pjr->hPrinter,
pjr->dwJobId,
1,
(LPBYTE)pji1Job,
0))
{
Status = GetLastError();
DBGPRINT(("SetJob fails with %d\n",Status));
}
return Status;
}
struct commtable
{
PSZ commentstr;
DWORD (near *pfnHandle)(PJR);
} commtable [] =
{
{ FORCOMMENT, HandleFor },
{ TITLECOMMENT, HandleTitle },
{ BEXITSERVER, HandleBeginExitServer },
{ EEXITSERVER, HandleEndExitServer },
{ BPROCSET, HandleBeginProcSet },
{ EPROCSET, HandleEndProcSet },
{ INCLUDEPROCSET, HandleIncludeProcSet },
{ CREATIONDATE, HandleCreationDate },
{ CREATOR, HandleCreator },
{ EOFCOMMENT, HandleEOF },
{ LOGIN, HandleLogin },
{ LOGINCONT, HandleLogin },
{ BEGINBINARY, HandleBeginBinary },
{ ENDBINARY, HandleEndBinary },
{ PAGESCOMMENT, HandlePages },
{ NULL, NULL }
};
/*
** HandleComment()
**
** Purpose: Handles Comment Events.
**
*/
DWORD
HandleComment(
PJR pjr,
PBYTE ps
)
{
PSZ token;
struct commtable *pct;
DWORD status = NO_ERROR;
DBGPRINT(("Enter HandleComment\n"));
if ((token = strtok(ps," :")) != NULL)
{
DBGPRINT(("Comment: %s\n", token));
for (pct = commtable; pct->pfnHandle; pct++)
{
if (!_stricmp(token, pct->commentstr))
{
status = pct->pfnHandle(pjr);
break;
}
}
}
// No action on this keyword !!!
return status;
}
/*
** HandleJobComment()
**
** Purpose: This parses PostScript Job Comments
*/
void
HandleJobComment(
PJR pjr,
PBYTE ps
)
{
char *token;
DBGPRINT(("Enter HandleJobComment\n"));
token= strtok(ps, " ");
//
// it's a job statement
//
if ((token = strtok(NULL, " ")) != NULL)
{
/* standard job identification */
if (!strcmp(token, QUERYJOBID))
{
pjr->psJobState = psQueryJob;
pjr->JSState = JSStrip;
DBGPRINT(("This is a standard job\n"));
return;
}
if (!strcmp(token, EXITJOBID))
{
pjr->psJobState = psExitServerJob;
pjr->JSState = JSStrip;
DBGPRINT(("This is an exitjob\n"));
return;
}
}
//
// Job identification not recognized, but some PostScript hackers
// put the program name in this comment, so we treat this as a standard
// job
//
DBGPRINT(("This is an unknown jobtype - processing as standard job\n"));
pjr->psJobState = psStandardJob;
pjr->JSState = JSWrite;
}
/* LineLength -
* Returns the number of bytes, including CR/LF to the next
* CR/LF in the buffer. If no CR/LF found, returns -1
*/
int
LineLength(PBYTE pBuf, int cbBuf)
{
int intLength = 0;
while (intLength < cbBuf)
{
//
// we are looking for a CR
//
if (pBuf[intLength] != '\x0d')
{
intLength++;
continue;
}
//
// we've found a CR. If it's followed by a LF, return that
// length too, otherwise, just return what we've found
//
if ((intLength + 1) < cbBuf)
{
if (pBuf[intLength + 1] == '\x0a')
{
return intLength + 2;
}
}
return intLength + 1;
}
return (-1);
}
/*
**
** PSParse()
**
** Purpose: This does the actual parsing of the PostScript Data Stream.
** This routine is always called pointing to the data stream at
** the beginning of a the Data Stream, or the beginning of a line.
**
** Returns: PAPWrite error codes.
**
*/
DWORD
PSParse(
PJR pjr,
PBYTE pchbuf,
int cchlen
)
{
int cbskip;
char ps[PENDLEN];
DWORD err = NO_ERROR;
DBGPRINT(("ENTER: PSParse()\n"));
while (cchlen > 0)
{
if ((cbskip = LineLength(pchbuf, cchlen)) == -1)
return (MoveToPending(pjr, pchbuf, cchlen));
/* Determine what the event is */
if ((cbskip < PSLEN) && (pchbuf[0] == '%'))
{
/* copy a comment into the ps string */
memcpy(ps, pchbuf, cbskip);
ps[cbskip-1] = 0; // OverWrite the CR/LF
if (ps[1] == '%')
{
/* Its a Query Comment */
if (ps[2] == '?'&& !pjr->InBinaryOp)
{
if (ps[3] == 'B')
{
/* Process the Begin Query Comment */
if ((err = HandleBQComment(pjr, ps)) != NO_ERROR)
{
DBGPRINT(("PSParse: HandleBQComment %ld\n", err));
return(err);
}
}
else if (ps[3] == 'E')
{
if (pjr->InProgress == QUERYDEFAULT)
{
if ((err = FinishDefaultQuery(pjr, ps)) != NO_ERROR)
{
DBGPRINT(("PSParse: FinishDefaultQuery %ld\n", err));
return(err);
}
}
}
}
else
{
/* Process the Comment */
if ((err = HandleComment(pjr, ps)) != NO_ERROR)
{
DBGPRINT(("PSParse: HandleComment %ld\n", err));
return(err);
}
}
}
else if (ps[1] == '!'&& !pjr->InBinaryOp)
{
/* Process Job ID Comment */
HandleJobComment(pjr, ps);
}
}
/* Write the lines to the spoolfile? */
if ((err = WriteToSpool (pjr, pchbuf, cbskip)) != NO_ERROR)
return (err);
pchbuf += cbskip;
cchlen -= cbskip;
}
return NO_ERROR;
}