You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
877 lines
26 KiB
877 lines
26 KiB
/********************************************************************/
|
|
/** Microsoft NT printing - separator pages **/
|
|
/********************************************************************/
|
|
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
|
|
#define _CTYPE_DISABLE_MACROS
|
|
#include <wingdip.h>
|
|
#include <winbasep.h>
|
|
|
|
|
|
/* this is max. no. of chars to be printed on a line */
|
|
/* these numbers must be taken from somewhere else */
|
|
#define MAXLINE 256
|
|
#define DEFAULT_LINE_WIDTH 80
|
|
|
|
#define BLOCK_CHAR_HEIGHT 16
|
|
#define BLOCK_CHAR_WIDTH 8
|
|
#define BLOCK_CHAR_DWIDTH 16
|
|
|
|
#define NORMAL_MODE 'U'
|
|
#define BLOCK_START 'B'
|
|
#define SINGLE_WIDTH 'S'
|
|
#define DOUBLE_WIDTH 'M'
|
|
#define TEXT_MODE 'L'
|
|
#define WIDTH_CHANGE 'W'
|
|
#define END_PAGE 'E'
|
|
#define FILE_INSERT 'F'
|
|
#define USER_NAME 'N'
|
|
#define JOB_ID 'I'
|
|
#define DATE_INSERT 'D'
|
|
#define TIME_INSERT 'T'
|
|
#define HEX_CODE 'H'
|
|
|
|
/* global structure (instance data) */
|
|
typedef struct {
|
|
PSPOOL pSpool;
|
|
HANDLE hFile;
|
|
HANDLE hFileMapping;
|
|
DWORD dwFileCount;
|
|
DWORD dwFileSizeLo;
|
|
DWORD cbOutBufLength;
|
|
DWORD cbLineLength;
|
|
DWORD linewidth;
|
|
char *OutBuf;
|
|
char *pOutBufPos;
|
|
char *pNextFileChar;
|
|
char *pFileStart;
|
|
char mode;
|
|
char cEsc;
|
|
char cLastChar; // Used to store DBCS lead byte.
|
|
HDC hDCMem; // Used to create Kanji banner char.
|
|
HFONT hFont; // Used to create Kanji banner char.
|
|
HBITMAP hBitmap; // Used to create Kanji banner char.
|
|
PVOID pvBits; // Used to create Kanji nanner char.
|
|
} GLOBAL_SEP_DATA;
|
|
|
|
/* static variables */
|
|
static char *szDefaultSep = "@@B@S@N@4 @B@S@I@4 @U@L @D@1 @E";
|
|
static char *sznewline = "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n";
|
|
static LPWSTR szDefaultSepName = L"DEFAULT.SEP";
|
|
|
|
/* Forward declarations */
|
|
int OpenSepFile(GLOBAL_SEP_DATA *, LPWSTR);
|
|
int CloseSepFile(GLOBAL_SEP_DATA *);
|
|
int ReadSepChar(GLOBAL_SEP_DATA *);
|
|
void UngetSepChar(GLOBAL_SEP_DATA *, int);
|
|
int WriteSepBuf(GLOBAL_SEP_DATA *, char *, DWORD);
|
|
int DoSeparatorPage(GLOBAL_SEP_DATA *);
|
|
int AddNormalChar(GLOBAL_SEP_DATA *, int);
|
|
int AddBlockChar(GLOBAL_SEP_DATA *, int);
|
|
int FlushOutBuf(GLOBAL_SEP_DATA *);
|
|
int FlushNewLine(GLOBAL_SEP_DATA *);
|
|
void ReadFileName(GLOBAL_SEP_DATA *, char *, DWORD);
|
|
int ConvertAtoH(int);
|
|
void ConvertTimetoChar(LPSYSTEMTIME,char *);
|
|
void ConvertDatetoChar(LPSYSTEMTIME,char *);
|
|
|
|
/**************************************************************\
|
|
** DoSeparator(pSpool)
|
|
** This function is called by the spooler. It is the
|
|
** entry point for the separator page code. It opens the
|
|
** separator page file, processes it, sends the output
|
|
** directly to the printer, and then returns control
|
|
** to the spooler.
|
|
**
|
|
** RETURN VALUE: 1 = OK, 0 = error
|
|
\**************************************************************/
|
|
int DoSeparator(
|
|
PSPOOL pSpool
|
|
)
|
|
|
|
{
|
|
GLOBAL_SEP_DATA g = {0};
|
|
int status;
|
|
|
|
g.pSpool = pSpool;
|
|
|
|
if (!OpenSepFile(&g, pSpool->pIniJob->pIniPrinter->pSepFile)) {
|
|
return(0);
|
|
}
|
|
//
|
|
// We used to call OpenProfileUserMapping() and CloseProfileUserMapping()
|
|
// before and after DoSeparatorPage. But they are not multi thread safe
|
|
// and are not needed now that we use SystemTimeToTzSpecificLocalTime
|
|
// instead of GetProfileInt etc..
|
|
//
|
|
status = DoSeparatorPage(&g);
|
|
CloseSepFile(&g);
|
|
|
|
if (!status) {
|
|
return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** OpenSepFile(pg, szFileName)
|
|
** open file for input.
|
|
** at the moment, this does nothing--stdin and stdout are used
|
|
\**************************************************************/
|
|
int OpenSepFile(
|
|
GLOBAL_SEP_DATA *pg,
|
|
LPWSTR szFileName
|
|
)
|
|
{
|
|
if (!lstrcmpi(szFileName, szDefaultSepName)) {
|
|
/* if szFileName is empty, just use default separator page string */
|
|
pg->hFile = NULL;
|
|
pg->hFileMapping = NULL;
|
|
pg->pFileStart = pg->pNextFileChar = szDefaultSep;
|
|
pg->dwFileSizeLo = strlen(szDefaultSep);
|
|
}
|
|
else {
|
|
HANDLE hImpersonationToken = RevertToPrinterSelf();
|
|
|
|
/* otherwise, open the file */
|
|
pg->hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
ImpersonatePrinterClient(hImpersonationToken);
|
|
|
|
if (pg->hFile==INVALID_HANDLE_VALUE) {
|
|
return(0);
|
|
}
|
|
pg->dwFileSizeLo = GetFileSize(pg->hFile, NULL); /* assume < 4 GB! */
|
|
pg->hFileMapping = CreateFileMapping(pg->hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (!pg->hFileMapping || pg->dwFileSizeLo==-1) {
|
|
CloseSepFile(pg);
|
|
return(0);
|
|
}
|
|
pg->pFileStart =
|
|
pg->pNextFileChar = (char *)
|
|
MapViewOfFile(pg->hFileMapping, FILE_MAP_READ, 0, 0, pg->dwFileSizeLo);
|
|
if (!pg->pFileStart) {
|
|
CloseSepFile(pg);
|
|
return(0);
|
|
}
|
|
} /* end of else (szFileName non-NULL) */
|
|
|
|
pg->dwFileCount = 0;
|
|
|
|
/* now, allocate local buffer for output */
|
|
pg->OutBuf = (char *)AllocSplMem( BLOCK_CHAR_HEIGHT*(MAXLINE+2) );
|
|
if (!pg->OutBuf) {
|
|
CloseSepFile(pg);
|
|
return(0);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** CloseSepFile(pg)
|
|
** close files.
|
|
\**************************************************************/
|
|
int CloseSepFile(GLOBAL_SEP_DATA *pg)
|
|
{
|
|
if (pg->OutBuf) {
|
|
FreeSplMem(pg->OutBuf);
|
|
}
|
|
if (pg->hFileMapping) {
|
|
if (pg->pFileStart) {
|
|
UnmapViewOfFile(pg->pFileStart);
|
|
}
|
|
CloseHandle(pg->hFileMapping);
|
|
}
|
|
if (pg->hFile) {
|
|
CloseHandle(pg->hFile);
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** ReadSepChar(pg)
|
|
** reads a character from the separator file and returns it
|
|
\**************************************************************/
|
|
int ReadSepChar(GLOBAL_SEP_DATA *pg)
|
|
{
|
|
if (pg->dwFileCount >= pg->dwFileSizeLo) {
|
|
return(EOF);
|
|
}
|
|
pg->dwFileCount++;
|
|
return(*pg->pNextFileChar++);
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** UngetSepChar(pg, c)
|
|
** ungets a character to the separator file
|
|
\**************************************************************/
|
|
void UngetSepChar(
|
|
GLOBAL_SEP_DATA *pg,
|
|
int c
|
|
)
|
|
{
|
|
if (c != EOF && pg->dwFileCount) {
|
|
pg->dwFileCount--;
|
|
pg->pNextFileChar--;
|
|
}
|
|
}
|
|
|
|
/**************************************************************\
|
|
** WriteSepBuf(pg, str, cb)
|
|
** write cb bytes of a string to the printer
|
|
\**************************************************************/
|
|
int WriteSepBuf(
|
|
GLOBAL_SEP_DATA *pg,
|
|
char *str,
|
|
DWORD cb
|
|
)
|
|
{
|
|
DWORD cbWritten;
|
|
|
|
return(LocalWritePrinter(pg->pSpool, str, cb, &cbWritten)
|
|
&& (cbWritten==cb)
|
|
);
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** FlushOutBuf(pg)
|
|
** flush the output buffer (block or line mode)
|
|
** WHAT'S TRICKY HERE IS THAT IF WE'RE IN LINE MODE, WE SIMPLY
|
|
** WRITE THE STUFF TO THE FILE, WHEREAS IF WE'RE IN BLOCK
|
|
** CHARACTER MODE, WE FORCE CARRIAGE-RETURN / LINEFEEDS ON
|
|
** EACH OF THE EIGHT BUFFERED LINES THAT MAKE UP THE BLOCK
|
|
** CHARACTERS; i.e., FlushOutBuf() SERVES AS AN EOL IN BLOCK
|
|
** MODE, BUT NOT IN LINE MODE.
|
|
**
|
|
** - return TRUE means ok
|
|
** - return FALSE means problem
|
|
\**************************************************************/
|
|
int FlushOutBuf(GLOBAL_SEP_DATA *pg)
|
|
{
|
|
int i,status = TRUE;
|
|
char *pBlkLine;
|
|
|
|
if (!pg->cbOutBufLength) {
|
|
return(TRUE);
|
|
}
|
|
if (pg->mode == NORMAL_MODE) {
|
|
/* write out entire buffer at once */
|
|
status = WriteSepBuf(pg, pg->OutBuf, pg->cbOutBufLength);
|
|
}
|
|
else {
|
|
/* BLOCK MODE:
|
|
* force carriage-return and linefeed on all eight lines
|
|
*/
|
|
pBlkLine = pg->OutBuf;
|
|
for (i=0; (i < BLOCK_CHAR_HEIGHT) && status; i++) {
|
|
*pg->pOutBufPos = '\r';
|
|
*(pg->pOutBufPos+1) = '\n';
|
|
status = WriteSepBuf(pg, pBlkLine, pg->cbLineLength+2);
|
|
pg->pOutBufPos += MAXLINE+2;
|
|
pBlkLine += MAXLINE+2;
|
|
}
|
|
pg->cbLineLength = 0;
|
|
}
|
|
|
|
pg->pOutBufPos = pg->OutBuf;
|
|
pg->cbOutBufLength = 0;
|
|
return(status);
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** FlushNewLine(pg)
|
|
** Starts a new line: if BLOCK MODE, just do FlushOutBuf();
|
|
** if not, send a '\r' '\n' combination, then flush.
|
|
** - return TRUE means ok
|
|
** - return FALSE means problem
|
|
\**************************************************************/
|
|
int FlushNewLine(GLOBAL_SEP_DATA *pg)
|
|
{
|
|
if (pg->mode==NORMAL_MODE && pg->cbLineLength) {
|
|
if (!AddNormalChar(pg,'\r')) return(FALSE);
|
|
if (!AddNormalChar(pg,'\n')) return(FALSE);
|
|
}
|
|
return(FlushOutBuf(pg));
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** AddNormalChar(pg, c)
|
|
** add a character to the output buffer (not block mode)
|
|
** - return TRUE means ok
|
|
** - return FALSE means problem
|
|
\**************************************************************/
|
|
int AddNormalChar(
|
|
GLOBAL_SEP_DATA *pg,
|
|
int c
|
|
)
|
|
{
|
|
if (c=='\n') {
|
|
/* reset line length count */
|
|
pg->cbLineLength = 0;
|
|
}
|
|
else {
|
|
if (isprint(c) && (++(pg->cbLineLength) > pg->linewidth)) {
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
*pg->pOutBufPos++ = (CHAR) c;
|
|
if (++(pg->cbOutBufLength) == BLOCK_CHAR_HEIGHT*(MAXLINE+2)) {
|
|
return(FlushOutBuf(pg));
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
} /* end of AddNormalChar() */
|
|
|
|
|
|
/**************************************************************\
|
|
** AddBlockChar(pg, c)
|
|
** add a character to the output buffer (block mode)
|
|
** return TRUE means ok
|
|
** return FALSE means problem
|
|
\**************************************************************/
|
|
int AddBlockChar(
|
|
GLOBAL_SEP_DATA *pg,
|
|
int c
|
|
)
|
|
{
|
|
int w;
|
|
register int i,k;
|
|
register char *p;
|
|
unsigned char cBits, *pcBits;
|
|
char cBlkFill;
|
|
register int j;
|
|
unsigned char *pcBitsLine;
|
|
HBITMAP hBitmapOld;
|
|
HFONT hFontOld;
|
|
CHAR aTextBuf[2];
|
|
SHORT sTextIndex = 0;
|
|
ULONG cjBitmap;
|
|
ULONG cjWidth = BLOCK_CHAR_WIDTH;
|
|
|
|
#define CJ_DIB16_SCAN(cx) ((((cx) + 15) & ~15) >> 3)
|
|
#define CJ_DIB16( cx, cy ) (CJ_DIB16_SCAN(cx) * (cy))
|
|
|
|
if( pg->cLastChar == (CHAR)NULL && IsDBCSLeadByte((CHAR)c) ) {
|
|
pg->cLastChar = (CHAR) c;
|
|
return(TRUE);
|
|
}
|
|
|
|
if(pg->hDCMem == NULL) {
|
|
pg->hDCMem = CreateCompatibleDC(NULL);
|
|
if (pg->hDCMem == NULL)
|
|
{
|
|
//
|
|
// Only happens when memory is exhausted. Functionality may suffer
|
|
// but we won't AV.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if(pg->hBitmap == NULL) {
|
|
pg->hBitmap = CreateCompatibleBitmap(pg->hDCMem,BLOCK_CHAR_DWIDTH,BLOCK_CHAR_HEIGHT);
|
|
if ( pg->hBitmap == NULL )
|
|
{
|
|
//
|
|
// Only happens when memory is exhausted. Functionality may suffer
|
|
// but we won't AV.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if(pg->pvBits == NULL) {
|
|
pg->pvBits = AllocSplMem(CJ_DIB16(BLOCK_CHAR_DWIDTH,BLOCK_CHAR_HEIGHT));
|
|
if ( pg->pvBits == NULL )
|
|
{
|
|
//
|
|
// Only happens when memory is exhausted. Functionality may suffer
|
|
// but we won't AV.
|
|
//
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if(pg->hFont == NULL) {
|
|
LOGFONT lf;
|
|
|
|
ZeroMemory(&lf, sizeof(lf));
|
|
|
|
lf.lfHeight = BLOCK_CHAR_HEIGHT;
|
|
lf.lfWidth = ( pg->mode == DOUBLE_WIDTH ) ?
|
|
BLOCK_CHAR_DWIDTH :
|
|
BLOCK_CHAR_WIDTH;
|
|
|
|
lf.lfWeight = FW_NORMAL;
|
|
lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
|
|
lf.lfCharSet = DEFAULT_CHARSET;
|
|
|
|
pg->hFont = CreateFontIndirect(&lf);
|
|
}
|
|
|
|
hBitmapOld = SelectObject(pg->hDCMem,pg->hBitmap);
|
|
hFontOld = SelectObject(pg->hDCMem,pg->hFont);
|
|
|
|
if( pg->cLastChar != (CHAR) NULL ) {
|
|
aTextBuf[sTextIndex] = pg->cLastChar;
|
|
sTextIndex ++;
|
|
cjWidth = BLOCK_CHAR_DWIDTH;
|
|
}
|
|
|
|
aTextBuf[sTextIndex] = (CHAR) c;
|
|
|
|
PatBlt(pg->hDCMem,0,0,BLOCK_CHAR_DWIDTH,BLOCK_CHAR_HEIGHT,WHITENESS);
|
|
TextOutA(pg->hDCMem,0,0,aTextBuf,sTextIndex+1);
|
|
|
|
GetBitmapBits(pg->hBitmap,CJ_DIB16(cjWidth,BLOCK_CHAR_HEIGHT),pg->pvBits);
|
|
|
|
SelectObject(pg->hDCMem,hBitmapOld);
|
|
SelectObject(pg->hDCMem,hFontOld);
|
|
|
|
w = (pg->mode==DOUBLE_WIDTH)? cjWidth * 2 : cjWidth;
|
|
if (pg->cbLineLength+w > pg->linewidth) {
|
|
return(TRUE);
|
|
}
|
|
|
|
cBlkFill = '#';
|
|
|
|
pcBitsLine = (unsigned char *) pg->pvBits;
|
|
for (i = 0 ;
|
|
i < BLOCK_CHAR_HEIGHT;
|
|
i++, pcBitsLine += CJ_DIB16_SCAN(BLOCK_CHAR_DWIDTH)) {
|
|
|
|
/* put block character into buffer line by line, top first */
|
|
|
|
pcBits = pcBitsLine;
|
|
|
|
p = pg->pOutBufPos + i * (MAXLINE+2);
|
|
|
|
cBits = *pcBits;
|
|
j = 0;
|
|
for (k = cjWidth; k--; ) {
|
|
if (pg->mode==DOUBLE_WIDTH) {
|
|
*p = *(p+1) = (cBits & 0x80)? ' ' : cBlkFill;
|
|
p += 2;
|
|
} else {
|
|
*p++ = (cBits & 0x80)? ' ' : cBlkFill;
|
|
}
|
|
cBits <<= 1;
|
|
j++;
|
|
if( j==8 ) {
|
|
pcBits++; cBits = *pcBits; j = 0;
|
|
}
|
|
}
|
|
|
|
} /* end of loop through lines of block char */
|
|
|
|
pg->cLastChar = (CHAR) NULL;
|
|
pg->pOutBufPos += w;
|
|
pg->cbLineLength += w;
|
|
pg->cbOutBufLength += w;
|
|
return(TRUE);
|
|
|
|
} /* end of AddBlockChar() */
|
|
|
|
|
|
/**************************************************************\
|
|
** DoSeparatorPage(pg)
|
|
** this is the actual processing
|
|
\**************************************************************/
|
|
int DoSeparatorPage(GLOBAL_SEP_DATA *pg)
|
|
{
|
|
int status = TRUE;
|
|
int c;
|
|
char *pchar;
|
|
WCHAR *pwchar;
|
|
char tempbuf[MAX_PATH]; /* assume length of date, time, or job_id < MAXPATH */
|
|
int (*AddCharFxn)() = AddNormalChar;
|
|
|
|
if ((c = ReadSepChar(pg))==EOF) {
|
|
return(TRUE);
|
|
}
|
|
pg->linewidth = DEFAULT_LINE_WIDTH;
|
|
pg->cEsc = (CHAR) c;
|
|
pg->pOutBufPos = pg->OutBuf;
|
|
pg->cbOutBufLength = 0;
|
|
pg->cbLineLength = 0;
|
|
pg->mode = NORMAL_MODE;
|
|
pg->hDCMem = (HDC) NULL;
|
|
pg->hFont = (HFONT) NULL;
|
|
pg->hBitmap = (HBITMAP) NULL;
|
|
pg->cLastChar = (CHAR) NULL;
|
|
pg->pvBits = (PVOID) NULL;
|
|
|
|
while (status && ((c=ReadSepChar(pg))!=EOF) ) {
|
|
|
|
/* find the next escape sequence */
|
|
if (c != pg->cEsc) continue;
|
|
|
|
/* found an escape character: now, check the next character */
|
|
if ((c=ReadSepChar(pg))==EOF) {
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case TEXT_MODE:
|
|
if (pg->mode==NORMAL_MODE) {
|
|
while (status && ((c=ReadSepChar(pg)) != EOF)) {
|
|
if (c!=pg->cEsc) {
|
|
status = AddNormalChar(pg, c);
|
|
}
|
|
else {
|
|
/* This is to treat <esc><esc> as a normal char */
|
|
c = ReadSepChar(pg);
|
|
if (c==pg->cEsc) {
|
|
status = AddNormalChar(pg, c);
|
|
}
|
|
else {
|
|
UngetSepChar(pg, c);
|
|
UngetSepChar(pg, pg->cEsc);
|
|
break; /* breaks from the while, returns to main loop */
|
|
}
|
|
}
|
|
}
|
|
} /* end of NORMAL_MODE processing */
|
|
|
|
else {
|
|
while (status && ((c=ReadSepChar(pg))!=EOF)) {
|
|
if (c=='\n') {
|
|
status = FlushOutBuf(pg);
|
|
}
|
|
else if (c=='\r') {
|
|
/* if followed by '\n', ignore.
|
|
* Otherwise, AddBlockChar() the '\r'.
|
|
*/
|
|
c = ReadSepChar(pg);
|
|
if (c!='\n') {
|
|
status = AddBlockChar(pg, '\r');
|
|
}
|
|
UngetSepChar(pg, c);
|
|
}
|
|
else {
|
|
if (c==pg->cEsc) {
|
|
/* This is to treat <esc><esc> as a normal char */
|
|
c = ReadSepChar(pg);
|
|
if (c==pg->cEsc) {
|
|
status = AddBlockChar(pg, c);
|
|
}
|
|
else {
|
|
UngetSepChar(pg, c);
|
|
UngetSepChar(pg, pg->cEsc);
|
|
break; /* breaks from the while, returns to main loop */
|
|
}
|
|
}
|
|
else {
|
|
status = AddBlockChar(pg, c);
|
|
}
|
|
}
|
|
}
|
|
} /* end of BLOCK mode processing */
|
|
|
|
break;
|
|
|
|
case BLOCK_START:
|
|
case SINGLE_WIDTH:
|
|
case DOUBLE_WIDTH:
|
|
case NORMAL_MODE:
|
|
status = FlushNewLine(pg);
|
|
pg->mode = (CHAR) c;
|
|
AddCharFxn = (pg->mode==NORMAL_MODE)? AddNormalChar : AddBlockChar;
|
|
break;
|
|
|
|
case USER_NAME:
|
|
pwchar = pg->pSpool->pIniJob->pUser;
|
|
|
|
if (pwchar) {
|
|
char *pchar;
|
|
UNICODE_STRING UnicodeString;
|
|
ANSI_STRING AnsiString;
|
|
|
|
RtlInitUnicodeString(&UnicodeString, pwchar);
|
|
RtlUnicodeStringToAnsiString(&AnsiString ,&UnicodeString, TRUE);
|
|
|
|
pchar = AnsiString.Buffer;
|
|
|
|
if ( pchar )
|
|
{
|
|
while (*pchar && status) status = (*AddCharFxn)(pg, *pchar++);
|
|
}
|
|
|
|
RtlFreeAnsiString(&AnsiString);
|
|
}
|
|
break;
|
|
|
|
case DATE_INSERT:
|
|
ConvertDatetoChar(&pg->pSpool->pIniJob->Submitted, tempbuf);
|
|
pchar = tempbuf;
|
|
while (*pchar && status) status = (*AddCharFxn)(pg, *pchar++);
|
|
break;
|
|
|
|
case TIME_INSERT:
|
|
ConvertTimetoChar(&pg->pSpool->pIniJob->Submitted, tempbuf);
|
|
pchar = tempbuf;
|
|
while (*pchar && status) status = (*AddCharFxn)(pg, *pchar++);
|
|
break;
|
|
|
|
case JOB_ID:
|
|
|
|
StringCchPrintfA(tempbuf, COUNTOF(tempbuf), "%d", pg->pSpool->pIniJob->JobId);
|
|
|
|
pchar = tempbuf;
|
|
while (*pchar && status) status = (*AddCharFxn)(pg, *pchar++);
|
|
break;
|
|
|
|
case HEX_CODE:
|
|
/* print a control character--read the hexadecimal code */
|
|
|
|
c = ReadSepChar(pg);
|
|
if (isxdigit(c)) {
|
|
int c2 = ReadSepChar(pg);
|
|
if (isxdigit(c2)) {
|
|
c = (char)((ConvertAtoH(c) << 4) + ConvertAtoH(c2));
|
|
status = (*AddCharFxn)(pg, c);
|
|
}
|
|
else {
|
|
UngetSepChar(pg, c2);
|
|
/* perhaps shouldn't do this? If they say @Hxx,
|
|
* implying xx is a hexadecimal code, and the second
|
|
* x is not a hex digit, should we leave that char
|
|
* on the input line to be interpreted next, or should
|
|
* we skip it? This only matters if it was an escape char,
|
|
* i.e. @Hx@.... Right now, the second @ is considered
|
|
* the start of a new command, and the @Hx is ignored
|
|
* entirely. The same applies for the UngetSepChar() below.
|
|
*/
|
|
}
|
|
}
|
|
else {
|
|
UngetSepChar(pg, c);
|
|
}
|
|
break;
|
|
|
|
case WIDTH_CHANGE:
|
|
{
|
|
/* read the decimal number; change line width if reasonable */
|
|
int new_width = 0;
|
|
|
|
for (c = ReadSepChar(pg); isdigit(c); c = ReadSepChar(pg)) {
|
|
new_width = 10 * new_width + c - '0';
|
|
}
|
|
UngetSepChar(pg, c);
|
|
|
|
if (new_width <= MAXLINE) {
|
|
pg->linewidth = new_width;
|
|
}
|
|
else {
|
|
pg->linewidth = MAXLINE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case '9':
|
|
case '8':
|
|
case '7':
|
|
case '6':
|
|
case '5':
|
|
case '4':
|
|
case '3':
|
|
case '2':
|
|
case '1':
|
|
case '0':
|
|
if (pg->mode==NORMAL_MODE) {
|
|
status = AddNormalChar(pg,'\n');
|
|
}
|
|
if (status) status = FlushOutBuf(pg);
|
|
if (status) status = WriteSepBuf(pg, sznewline, 2*(c-'0'));
|
|
break;
|
|
|
|
case END_PAGE:
|
|
/* this just outputs a formfeed character */
|
|
status = FlushNewLine(pg);
|
|
if (status) status = WriteSepBuf(pg, "\f",1);
|
|
break;
|
|
|
|
case FILE_INSERT:
|
|
{
|
|
HANDLE hFile2, hMapping2;
|
|
DWORD dwSizeLo2;
|
|
char *pFirstChar;
|
|
HANDLE hImpersonationToken;
|
|
|
|
if (!(status = FlushNewLine(pg))) {
|
|
break;
|
|
}
|
|
ReadFileName(pg, tempbuf, sizeof(tempbuf));
|
|
|
|
hImpersonationToken = RevertToPrinterSelf();
|
|
|
|
hFile2 = CreateFileA(tempbuf, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
|
|
|
|
ImpersonatePrinterClient(hImpersonationToken);
|
|
|
|
if (hFile2 != INVALID_HANDLE_VALUE) {
|
|
dwSizeLo2 = GetFileSize(hFile2, NULL); /* assume < 4 gigabytes! */
|
|
hMapping2 = CreateFileMapping(hFile2,NULL,PAGE_READONLY,0,0,NULL);
|
|
if (hMapping2 && (dwSizeLo2 > 0)) {
|
|
pFirstChar = (char *)
|
|
MapViewOfFile(hMapping2, FILE_MAP_READ, 0, 0, dwSizeLo2);
|
|
if (pFirstChar) {
|
|
status = WriteSepBuf(pg, pFirstChar, dwSizeLo2);
|
|
UnmapViewOfFile(pFirstChar);
|
|
}
|
|
CloseHandle(hMapping2);
|
|
}
|
|
CloseHandle(hFile2);
|
|
}
|
|
|
|
/* NOTE: if couldn't open file, or error while reading file,
|
|
* status is NOT set to false. We will simply stop the file
|
|
* insert operation, and continue processing the rest of the
|
|
* the separator page as before.
|
|
*/
|
|
else {
|
|
DBGMSG(DBG_WARNING, ("SEPARATOR PAGE: Could not open file %s \n",tempbuf));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
} /* end of main while loop...find next escape sequence, process */
|
|
|
|
if (status) status = FlushOutBuf(pg);
|
|
|
|
if (pg->hDCMem != (HDC) NULL) DeleteDC(pg->hDCMem);
|
|
if (pg->hFont != (HFONT) NULL) DeleteObject(pg->hFont);
|
|
if (pg->hBitmap != (HBITMAP) NULL) DeleteObject(pg->hBitmap);
|
|
if (pg->pvBits != (PVOID) NULL) FreeSplMem(pg->pvBits);
|
|
|
|
return(status);
|
|
|
|
} /* end of DoSeparatorPage() */
|
|
|
|
|
|
/**************************************************************\
|
|
** ConvertAtoH(c)
|
|
** Converts an ASCII character to hexadecimal.
|
|
\**************************************************************/
|
|
int ConvertAtoH(int c)
|
|
{
|
|
return( c - (isdigit(c)? '0' :
|
|
((isupper(c)? 'A':'a') - 10)));
|
|
}
|
|
|
|
|
|
/**************************************************************\
|
|
** ConvertTimetoChar()
|
|
** converts system time to a string (internationalized).
|
|
\**************************************************************/
|
|
void ConvertTimetoChar(
|
|
SYSTEMTIME *pSystemTime,
|
|
char *string
|
|
)
|
|
{
|
|
SYSTEMTIME LocalTime;
|
|
LCID lcid;
|
|
// Convert to local time
|
|
SystemTimeToTzSpecificLocalTime(NULL, pSystemTime, &LocalTime);
|
|
// Get lcid of local machine
|
|
lcid=GetSystemDefaultLCID();
|
|
// Convert to string, , using default format for that locale
|
|
|
|
GetTimeFormatA(lcid, 0, &LocalTime, NULL, string, MAX_PATH-1);
|
|
}
|
|
|
|
/**************************************************************\
|
|
** ConvertDatetoChar()
|
|
** converts system date to a string (internationalized).
|
|
\**************************************************************/
|
|
void ConvertDatetoChar(
|
|
SYSTEMTIME *pSystemTime,
|
|
char *string
|
|
)
|
|
{
|
|
SYSTEMTIME LocalTime;
|
|
LCID lcid;
|
|
// Convert to local time
|
|
SystemTimeToTzSpecificLocalTime(NULL, pSystemTime, &LocalTime);
|
|
// Get lcid of local machine
|
|
lcid = GetSystemDefaultLCID();
|
|
// Convert to string, using default format for that locale
|
|
GetDateFormatA(lcid, 0, &LocalTime, NULL, string, MAX_PATH-1);
|
|
}
|
|
|
|
/**************************************************************\
|
|
** ReadFileName(pg, szfilename, dwbufsize)
|
|
** parses a filename from the separator file (following <esc>F).
|
|
** the following scheme is used:
|
|
**
|
|
** - read until a single escape, EOF, newline, or carriage return
|
|
** is encountered. Put this string into a temporary buffer,
|
|
** passed by the calling function.
|
|
**
|
|
** - if string begins with a double quote, skip this double quote,
|
|
** and consider the double quote character as an end of string
|
|
** marker, just like the newline. Thus, @F"myfile
|
|
** will be read as @Fmyfile
|
|
**
|
|
\**************************************************************/
|
|
void ReadFileName(
|
|
GLOBAL_SEP_DATA *pg,
|
|
char *szfilename,
|
|
DWORD dwbufsize
|
|
)
|
|
{
|
|
char *pchar = szfilename;
|
|
char c;
|
|
DWORD dwcount = 0;
|
|
BOOL bNotQuote = TRUE;
|
|
|
|
if ((pg->dwFileCount < pg->dwFileSizeLo) && (*pg->pNextFileChar=='\"')) {
|
|
pg->dwFileCount++;
|
|
pg->pNextFileChar++;
|
|
bNotQuote = FALSE;
|
|
}
|
|
while ((dwcount < dwbufsize - 1) && (pg->dwFileCount < pg->dwFileSizeLo) && (c=*pg->pNextFileChar)!='\n'
|
|
&& c!='\r' && (bNotQuote || c!='\"')) {
|
|
if (c!=pg->cEsc) {
|
|
*pchar++ = c;
|
|
dwcount++;
|
|
pg->pNextFileChar++;
|
|
pg->dwFileCount++;
|
|
}
|
|
else {
|
|
if ((pg->dwFileCount+1) < pg->dwFileSizeLo
|
|
&& *(pg->pNextFileChar+1)==pg->cEsc) {
|
|
*pchar++ = pg->cEsc;
|
|
dwcount++;
|
|
pg->pNextFileChar+=2;
|
|
pg->dwFileCount+=2;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
} /* end of loop to read characters */
|
|
|
|
*pchar = '\0';
|
|
|
|
} /* end of ReadFileName() */
|