windows-nt/Source/XPSP1/NT/sdktools/rcdll/p0io.c
2020-09-26 16:20:57 +08:00

677 lines
22 KiB
C

/************************************************************************/
/* */
/* RCPP - Resource Compiler Pre-Processor for NT system */
/* */
/* P0IO.C - Input/Output for Preprocessor */
/* */
/* 27-Nov-90 w-BrianM Update for NT from PM SDK RCPP */
/* */
/************************************************************************/
#include "rc.h"
/************************************************************************/
/* Local Function Prototypes */
/************************************************************************/
PWCHAR esc_sequence(PWCHAR, PWCHAR);
#define TEXT_TYPE ptext_t
/*** ASSUME : the trailing marker byte is only 1 character. ***/
#define PUSHBACK_BYTES 1
#define TRAILING_BYTES 1
#define EXTRA_BYTES (PUSHBACK_BYTES + TRAILING_BYTES)
/*
** here are some defines for the new handling of io buffers.
** the buffer itself is 6k plus some extra bytes.
** the main source file uses all 6k.
** the first level of include files will use 4k starting 2k from the beginning.
** the 2nd level - n level will use 2k starting 4k from the beginning.
** this implies that some special handling may be necessary when we get
** overlapping buffers. (unless the source file itself is < 2k
** all the include files are < 2k and they do not nest more than 2 deep.)
** first, the source file is read into the buffer (6k at a time).
** at the first include file, (if the source from the parent file
** is more than 2k chars) . . .
** if the Current_char ptr is not pointing above the 2k boundary
** (which is the beginning of the buffer for the include file)
** then we pretend we've read in only 2k into the buffer and
** place the terminator at the end of the parents 2k buffer.
** else we pretend we've used up all chars in the parents buffer
** so the next read for the parent will be the terminator, and
** the buffer will get filled in the usual manner.
** (if we're in a macro, the picture is slightly different in that we have
** to update the 'real' source file pointer in the macro structure.)
**
** the first nested include file is handled in a similar manner. (except
** it starts up 4k away from the start.)
**
** any further nesting will keep overlaying the upper 2k part.
*/
#define IO_BLOCK (4 * 1024 + EXTRA_BYTES)
int vfCurrFileType = DFT_FILE_IS_UNKNOWN; //- Added for 16-bit file support.
extern expansion_t Macro_expansion[];
typedef struct s_filelist filelist_t;
static struct s_filelist { /* list of input files (nested) */
int fl_bufsiz; /* characters to read into the buffer */
FILE * fl_file; /* FILE id */
long fl_lineno; /* line number when file was pushed */
PWCHAR fl_name; /* previous file text name */
ptext_t fl_currc; /* ptr into our buffer for current c */
TEXT_TYPE fl_buffer; /* type of buffer */
int fl_numread; /* # of characters read from the file */
int fl_fFileType; //- Added for 16-bit file support.
//- return from DetermineFileType.
long fl_seek; //- Added for restart - contains seek
// address of last read.
} Fstack[LIMIT_NESTED_INCLUDES];
static FILE *Fp = NULL;
int Findex = -1;
/************************************************************************
* NEWINPUT - A new input file is to be opened, saving the old.
*
* ARGUMENTS
* WCHAR *newname - the name of the file
*
* RETURNS - none
*
* SIDE EFFECTS
* - causes input stream to be switched
* - Linenumber is reset to 1
* - storage is allocated for the newname
* - Filename is set to the new name
*
* DESCRIPTION
* The file is opened, and if successful, the current input stream is saved
* and the stream is switched to the new file. If the newname is NULL,
* then stdin is taken as the new input.
*
* AUTHOR - Ralph Ryan, Sept. 9, 1982
*
* MODIFICATIONS - none
*
************************************************************************/
int
newinput (
WCHAR *newname,
int m_open
)
{
filelist_t *pF;
WCHAR *p;
if( newname == NULL ) {
Fp = stdin;
}
else {
// Note: Always use the Ansi codepage here. uiCodePage may have been
// modified by a codepage pragma in the source file.
WideCharToMultiByte (GetACP(), 0, newname, -1, chBuf, sizeof(chBuf), NULL, NULL);
if((Fp = fopen(chBuf, "rb")) == NULL) {
if(m_open == MUST_OPEN) {
Msg_Temp = GET_MSG (1005);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, chBuf);
fatal(1005);
}
return(FALSE);
}
}
/* now push it onto the file stack */
++Findex;
if(Findex >= LIMIT_NESTED_INCLUDES) {
Msg_Temp = GET_MSG (1014);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, LIMIT_NESTED_INCLUDES);
fatal(1014);
}
pF = &Fstack[Findex];
p = (WCHAR *) MyAlloc((IO_BLOCK + PUSHBACK_BYTES) * sizeof(WCHAR));
if (!p) {
strcpy (Msg_Text, GET_MSG (1002));
fatal(1002); /* no memory */
return 0;
}
pF->fl_bufsiz = IO_BLOCK;
pF->fl_currc = Current_char; /* previous file's current char */
pF->fl_lineno = Linenumber; /* previous file's line number */
pF->fl_file = Fp; /* the new file descriptor */
pF->fl_buffer = p;
pF->fl_numread = 0;
pF->fl_seek = 0;
pF->fl_fFileType = DetermineFileType (Fp);
if (pF->fl_fFileType == DFT_FILE_IS_UNKNOWN) {
Msg_Temp = GET_MSG (4413);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, newname);
warning (4413);
pF->fl_fFileType = DFT_FILE_IS_8_BIT;
}
vfCurrFileType = pF->fl_fFileType;
Current_char = p;
io_eob(); /* fill the buffer */
/*
* Note that include filenames will live the entire compiland. This
* puts the burden on the user with MANY include files. Any other
* scheme takes space out of static data.
* Notice also, that we save the previous filename in the new file's
* fl_name.
*/
pF->fl_name = pstrdup(Filename);
wcsncpy(Filebuff, newname, sizeof(Filebuff) / sizeof(WCHAR));
Linenumber = 0; /* do_newline() will increment to the first line */
if(Eflag) {
emit_line();
// must manually write '\r' with '\n' when writing 16-bit strings
myfwrite(L"\r\n", 2 * sizeof(WCHAR), 1, OUTPUTFILE); /* this line is inserted */
}
{
defn_t d;
int old_line = Linenumber;
Linenumber = Findex;
DEFN_IDENT(&d) = L"!";
DEFN_TEXT(&d) = Reuse_Include;
DEFN_NEXT(&d) = NULL;
DEFN_NFORMALS(&d) = 0;
DEFN_EXPANDING(&d) = FALSE;
AfxOutputMacroDefn(&d);
if (Findex > 0) {
DEFN_IDENT(&d) = L"$";
DEFN_TEXT(&d) = Filename;
DEFN_NEXT(&d) = NULL;
DEFN_NFORMALS(&d) = 0;
DEFN_EXPANDING(&d) = FALSE;
AfxOutputMacroDefn(&d);
}
Linenumber = old_line;
}
do_newline(); /* a new file may have preproc cmd as first line */
return(TRUE);
}
/************************************************************************
* FPOP - pop to a previous level of input stream
*
* ARGUMENTS - none
*
* RETURNS
* TRUE if successful, FALSE if the stack is empty
*
* SIDE EFFECTS
* - Linenumber is restored to the old files line number
* - Filename is reset to the old filename
* - frees storage allocated for filename
*
* DESCRIPTION
* Pop the top of the file stack, restoring the previous input stream.
*
* AUTHOR - Ralph Ryan, Sept. 9, 1982
*
* MODIFICATIONS - none
*
************************************************************************/
WCHAR
fpop(
void
)
{
int OldLine;
defn_t DefType;
if(Findex == -1) { /* no files left */
return(FALSE);
}
if (Fp)
fclose(Fp);
OldLine = Linenumber;
--Findex;
Linenumber = Findex;
DEFN_IDENT(&DefType) = L"!";
DEFN_TEXT(&DefType) = L"";
DEFN_NEXT(&DefType) = NULL;
DEFN_NFORMALS(&DefType) = 0;
DEFN_EXPANDING(&DefType) = FALSE;
AfxOutputMacroDefn(&DefType);
Findex++;
Linenumber = OldLine;
strappend(Filebuff,Fstack[Findex].fl_name);
OldLine = Linenumber;
Linenumber = (int)Fstack[Findex].fl_lineno;
Current_char = Fstack[Findex].fl_currc;
MyFree(Fstack[Findex].fl_buffer);
if(--Findex < 0) { /* popped the last file */
Linenumber = OldLine;
return(FALSE);
}
Fp = Fstack[Findex].fl_file;
vfCurrFileType = Fstack[Findex].fl_fFileType;
if(Eflag) {
// If the last file didn't end in a \r\n, the #line from emit_line could
// end up in whatever data structure it ended in... Emit a dummy newline
// just in case.
myfwrite(L"\r\n", 2 * sizeof(WCHAR), 1, OUTPUTFILE); /* this line is inserted */
emit_line();
}
return(TRUE);
}
/************************************************************************
** nested_include : searches the parentage list of the currently
** open files on the stack when a new include file is found.
** Input : ptr to include file name.
** Output : TRUE if the file was found, FALSE if not.
*************************************************************************/
int
nested_include(
void
)
{
PWCHAR p_tmp1;
PWCHAR p_file;
PWCHAR p_slash;
int tos;
tos = Findex;
p_file = Filename; /* always start with the current file */
for(;;) {
p_tmp1 = p_file;
p_slash = NULL;
while(*p_tmp1) { /* pt to end of filename, find trailing slash */
if(wcschr(Path_chars, *p_tmp1)) {
p_slash = p_tmp1;
}
p_tmp1++;
}
if(p_slash) {
p_tmp1 = Reuse_W;
while(p_file <= p_slash) { /* we want the trailing '/' */
*p_tmp1++ = *p_file++; /* copy the parent directory */
}
p_file = yylval.yy_string.str_ptr;
while((*p_tmp1++ = *p_file++)!=0) { /*append include file name */
; /* NULL */
}
} else {
wcscpy(Reuse_W, yylval.yy_string.str_ptr);
}
if(newinput(Reuse_W,MAY_OPEN)) {
return(TRUE);
}
if(tos <= 0) {
break;
}
p_file = Fstack[tos--].fl_name;
}
return(FALSE);
}
/************************************************************************/
/* esc_sequence() */
/************************************************************************/
PWCHAR
esc_sequence(
PWCHAR dest,
PWCHAR name
)
{
*dest = L'"';
while((*++dest = *name) != 0) {
if (CHARMAP(*name) == LX_EOS) {
*++dest = L'\\';
}
name++;
}
*dest++ = L'"'; /* overwrite null */
return( dest );
}
/************************************************************************/
/* emit_line() */
/************************************************************************/
void
emit_line(
void
)
{
PWCHAR p;
swprintf(Reuse_W, L"#line %d ", Linenumber+1);
myfwrite(Reuse_W, wcslen(Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
p = esc_sequence(Reuse_W, Filename);
myfwrite(Reuse_W, (size_t)(p - Reuse_W) * sizeof(WCHAR), 1, OUTPUTFILE);
}
/************************************************************************
** io_eob : handle getting the next block from a file.
** return TRUE if this is the real end of the buffer, FALSE if we have
** more to do.
************************************************************************/
int
io_eob(
void
)
{
int n;
TEXT_TYPE p;
static int dc;
p = Fstack[Findex].fl_buffer;
if((Current_char - (ptext_t)p) < Fstack[Findex].fl_numread) {
/*
** haven't used all the chars from the buffer yet.
** (some clown has a null/cntl z embedded in his source file.)
*/
if(PREVCH() == CONTROL_Z) { /* imbedded control z, real eof */
UNGETCH();
return(TRUE);
}
return(FALSE);
}
Current_char = p;
//-
//- The following section was added to support 16-bit resource files.
//- It will just convert them to 8-bit files that the Resource Compiler
//- can read. Here is the basic strategy used. An 8-bit file is
//- read into the normal buffer and should be processed the old way.
//- A 16-bit file is read into a wide character buffer identical to the
//- normal 8-bit one. The entire contents are then copied to the 8-bit
//- buffer and processed normally. The one exception to this is when
//- a string literal is encountered. We then return to the 16-bit buffer
//- to read the characters. These characters are written as backslashed
//- escape characters inside an 8-bit string. (ex. "\x004c\x523f").
//- I'll be the first person to admit that this is an ugly solution, but
//- hey, we're Microsoft :-). 8-2-91 David Marsyla.
//-
if (Fstack[Findex].fl_fFileType == DFT_FILE_IS_8_BIT) {
REG int i;
REG PUCHAR lpb;
PUCHAR Buf;
Buf = (PUCHAR) MyAlloc(Fstack[Findex].fl_bufsiz + 1);
if (Buf == NULL) {
strcpy (Msg_Text, GET_MSG (1002));
fatal(1002); /* no memory */
}
Fstack[Findex].fl_seek = fseek(Fp, 0, SEEK_CUR);
n = fread (Buf, sizeof(char), Fstack[Findex].fl_bufsiz, Fp);
//-
//- Determine if the last byte is a DBCS lead byte
//- if YES (i will be greater than n), backup one byte
//-
for (i = 0, lpb = Buf; i < n; i++, lpb++) {
if (IsDBCSLeadByteEx(uiCodePage, *lpb)) {
i++;
lpb++;
}
}
if (i > n) {
fseek (Fp, -1, SEEK_CUR);
n--;
*(Buf + n) = 0;
}
//-
//- Convert the 8-bit buffer to the 16-bit buffer.
//-
Fstack[Findex].fl_numread = MultiByteToWideChar (uiCodePage, MB_PRECOMPOSED,
(LPCSTR) Buf, n, p, Fstack[Findex].fl_bufsiz);
MyFree (Buf);
} else {
Fstack[Findex].fl_numread = n =
fread (p, sizeof(WCHAR), Fstack[Findex].fl_bufsiz, Fp);
//-
//- If the file is in reversed format, swap the bytes.
//-
if (Fstack[Findex].fl_fFileType == DFT_FILE_IS_16_BIT_REV && n > 0) {
WCHAR *pT = p;
BYTE jLowNibble;
BYTE jHighNibble;
INT cNumWords = n;
while (cNumWords--) {
jLowNibble = (BYTE)(*pT & 0xFF);
jHighNibble = (BYTE)((*pT >> 8) & 0xFF);
*pT++ = (WCHAR)(jHighNibble | (jLowNibble << 8));
}
}
}
/*
** the total read counts the total read *and* used.
*/
if (n != 0) { /* we read something */
*(p + Fstack[Findex].fl_numread) = EOS_CHAR; /* sentinal at the end */
return(FALSE); /* more to do */
}
*p = EOS_CHAR; /* read no chars */
return(TRUE); /* real end of buffer */
}
/************************************************************************
** io_restart : restarts the current file with a new codepage
** Method: figure out where the current character came from
** using WideCharToMultiByte(...cch up to current char...)
** Note that this assumes that roundtrip conversion to/from
** Unicode results in the same # of characters out as in.
** fseek to the right place, then read a new buffer
**
** Note that uiCodePage controls the seek, so it must
** remain set to the value used to do the translation
** from multi-byte to Unicode until after io_restart returns.
**
************************************************************************/
int
io_restart(
unsigned long cp
)
{
int n;
TEXT_TYPE p;
// If it's a Unicode file, nothing to do, so just return.
if (Fstack[Findex].fl_fFileType != DFT_FILE_IS_8_BIT)
return TRUE;
p = Fstack[Findex].fl_buffer;
n = Fstack[Findex].fl_numread - (int)(Current_char - p);
if (n != 0) {
if (Fstack[Findex].fl_fFileType == DFT_FILE_IS_8_BIT) {
n = WideCharToMultiByte(uiCodePage, 0, Current_char, n, NULL, 0, NULL, NULL);
if (n == 0)
return TRUE;
} else
n *= sizeof(WCHAR);
fseek(Fp, -n, SEEK_CUR);
}
Fstack[Findex].fl_numread = 0;
// io_eob will return true if we're at the end of the file.
// this is an error for restart (it means there's nothing more
// to do here (ie: #pragma codepage is the last line in the file).
return !io_eob();
}
/************************************************************************
** p0_init : inits for prepocessing.
** Input : ptr to file name to use as input.
** ptr to LIST containing predefined values.
** ( -D's from cmd line )
**
** Note : if "newinput" cannot open the file,
** it gives a fatal msg and exits.
**
************************************************************************/
void
p0_init(
WCHAR *p_fname,
WCHAR *p_outname,
LIST *p_defns,
LIST *p_undefns
)
{
REG WCHAR *p_dstr;
REG WCHAR *p_eq;
int ntop;
SETCHARMAP(LX_FORMALMARK, LX_MACFORMAL);
SETCHARMAP(LX_FORMALSTR, LX_STRFORMAL);
SETCHARMAP(LX_FORMALCHAR, LX_CHARFORMAL);
SETCHARMAP(LX_NOEXPANDMARK, LX_NOEXPAND);
if(EXTENSION) {
/*
** '$' is an identifier character under extensions.
*/
SETCHARMAP(L'$', LX_ID);
SETCONTMAP(L'$', LXC_ID);
}
for(ntop = p_defns->li_top; ntop < MAXLIST; ++ntop) {
p_dstr = p_defns->li_defns[ntop];
p_eq = Reuse_W;
while ((*p_eq = *p_dstr++) != 0) { /* copy the name to Reuse_W */
if(*p_eq == L'=') { /* we're told what the value is */
break;
}
p_eq++;
}
if(*p_eq == L'=') {
WCHAR *p_tmp;
WCHAR *last_space = NULL;
*p_eq = L'\0'; /* null the = */
for(p_tmp = p_dstr; *p_tmp; p_tmp++) { /* find the end of it */
if(iswspace(*p_tmp)) {
if(last_space == NULL) {
last_space = p_tmp;
}
} else {
last_space = NULL;
}
}
if(last_space != NULL) {
*last_space = L'\0';
}
Reuse_W_hash = local_c_hash(Reuse_W);
Reuse_W_length = wcslen(Reuse_W) + 1;
if( *p_dstr ) { /* non-empty string */
definstall(p_dstr, (wcslen(p_dstr) + 2), FROM_COMMAND);
} else {
definstall((WCHAR *)0, 0, 0);
}
} else {
Reuse_W_hash = local_c_hash(Reuse_W);
Reuse_W_length = wcslen(Reuse_W) + 1;
definstall(L"1\000", 3, FROM_COMMAND); /* value of string is 1 */
}
}
/* undefine */
for(ntop = p_undefns->li_top; ntop < MAXLIST; ++ntop) {
p_dstr = p_undefns->li_defns[ntop];
p_eq = Reuse_W;
while ((*p_eq = *p_dstr++) != 0) { /* copy the name to Reuse_W */
if(*p_eq == L'=') { /* we're told what the value is */
break;
}
p_eq++;
}
if(*p_eq == L'=') {
WCHAR *p_tmp;
WCHAR *last_space = NULL;
*p_eq = L'\0'; /* null the = */
for(p_tmp = p_dstr; *p_tmp; p_tmp++) { /* find the end of it */
if(iswspace(*p_tmp)) {
if(last_space == NULL) {
last_space = p_tmp;
}
} else {
last_space = NULL;
}
}
if(last_space != NULL) {
*last_space = L'\0';
}
Reuse_W_hash = local_c_hash(Reuse_W);
Reuse_W_length = wcslen(Reuse_W) + 1;
if( *p_dstr ) { /* non-empty string */
undefine();
} else {
undefine();
}
} else {
Reuse_W_hash = local_c_hash(Reuse_W);
Reuse_W_length = wcslen(Reuse_W) + 1;
undefine(); /* value of string is 1 */
}
}
// Note: Always use the Ansi codepage here. uiCodePage may have been
// modified by a codepage pragma in the source file.
WideCharToMultiByte (GetACP(), 0, p_outname, -1, chBuf, sizeof(chBuf), NULL, NULL);
if ((OUTPUTFILE = fopen (chBuf, "w+b")) == NULL) {
Msg_Temp = GET_MSG (1023);
SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, chBuf);
fatal (1023);
}
newinput(p_fname,MUST_OPEN);
}
/************************************************************************
** p0_terminate : terminates prepocessing.
**
**
************************************************************************/
void
p0_terminate(
void
)
{
for ( ;fpop(); )
;
if (OUTPUTFILE)
fclose(OUTPUTFILE);
}