windows-nt/Source/XPSP1/NT/sdktools/nmake/inline.cpp
2020-09-26 16:20:57 +08:00

671 lines
18 KiB
C++

// INLINE.C - contains routines used to handle processing of in-line files
//
// Copyright (c) 1989-1990, Microsoft Corporation. All rights reserved.
//
// Purpose:
// This module contains the in-line file handling routines of NMAKE.
//
// Revision History:
// 04-Feb-2000 BTF Ported to Win64
// 15-Nov-1993 JdR Major speed improvements
// 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
// 01-Jun-1993 HV Use UngetTxtChr() instead of ungetc()
// 10-May-1993 HV Add include file mbstring.h
// Change the str* functions to STR*
// 02-Feb-1990 SB change fopen() to FILEOPEN()
// 03-Jan-1990 SB removed unitiallized variable
// 04-Dec-1989 SB Removed to unreferenced variables in makeInlineFiles()
// 01-Dec-1989 SB Changed realloc() to REALLOC()
// 22-Nov-1989 SB Changed free() to FREE()
// 07-Nov-1989 SB Length of action word not evaluated correct for multiple
// inline files for the same command
// 06-Nov-1989 SB allow macros in action word for inline files
// 24-Sep-1989 SB added processInline(), createInline()
// 20-Sep-1989 SB Created from routines previously scattered in the sources.
//
// Notes:
// Sections with 'NOTE:' inside comments marks important/incomplete items.
// NOTE: Function headers yet to be completed; other comments are incomplete
#include "precomp.h"
#pragma hdrstop
void processEschIn(char *);
// NOTE: This may go soon (use nextInlineFile ?)
void parseInlineFileList(char *);
// NOTE: The next one has to go soon
void appendScript(SCRIPTLIST**,SCRIPTLIST*);
void delInlineSymbol(char*);
char * nextInlineFile(char **);
// NOTE: Probably needs a new name
void replaceLtLt(char **, char *);
void createInline(FILE *, const char *, char **, BOOL);
char * getLine(char *, int);
void echoLine(char *, const char *, BOOL);
// NOTE: delScriptFiles() from nmake.c not yet brought in here
extern FILE * createDosTmp(char *);
char * makeInlineFiles(char *, char **, char **);
BOOL processInline(char *, char **, STRINGLIST **, BOOL);
// makeInlineFiles - creates memory images for in-line files
//
// Scope: Global.
//
// Purpose: This is the function that handles dynamic in-line files
//
// Input: s - Input command line string after first << (pts to char Buffer)
//
// Output: Returns ...
//
// Errors/Warnings:
// SYNTAX_UNEXPECTED_TOKEN - The makefile cannot end without the in-line file
// ending.
// CANT_READ_FILE - When the makefile is unreadable.
// SYNTAX_KEEP_INLINE_FILE - An inline file should end
// OUT_OF_MEMORY - On failing to extend in-memory in-line file.
//
// Uses Globals:
// file - global stream
// line - lexer's line count
//
// Notes:
// Usage notes and other important notes
char *
makeInlineFiles(
char *s,
char **begin,
char **end
)
{
char rgchBuf[MAXBUF];
char *t;
unsigned size;
BOOL fPastCmd = FALSE; // If seen line past Cmd line
// used when rgchBuf is insuff for in-memory-inline file
char *szTmpBuf = NULL;
_tcscpy(rgchBuf, "<<"); // to help parseInlineFileList
if (!getLine(rgchBuf+2,MAXBUF - 2)) {
if (feof(file))
makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
makeError(line, CANT_READ_FILE);
}
parseInlineFileList(rgchBuf);
for (;scriptFileList;scriptFileList = scriptFileList->next) {
for (;;) {
for (t = rgchBuf;;) {
*s++ = *t++;
if (s == *end) {
if (!szTmpBuf) { /* Increase size of s */
szTmpBuf = (char *) allocate(MAXBUF<<1);
_tcsncpy(szTmpBuf, *begin, MAXBUF);
s = szTmpBuf + MAXBUF;
size = MAXBUF << 1;
*end = szTmpBuf + size;
} else {
if ((size + MAXBUF < size) /* overflow error */
|| !(szTmpBuf = (char *) REALLOC(szTmpBuf,size+MAXBUF)))
makeError(line, MACRO_TOO_LONG);
s = szTmpBuf + size;
size += MAXBUF;
*end = szTmpBuf + size;
}
*begin = szTmpBuf;
}
if (!*t)
break;
}
if (fPastCmd && rgchBuf[0] == '<' && rgchBuf[1] == '<') {
//We don't care about action specified here; could be a macro
if (scriptFileList->next) {
if (!getLine(rgchBuf, MAXBUF)) {
if (feof(file))
makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
makeError(line, CANT_READ_FILE);
}
}
break;
}
fPastCmd = TRUE;
if (!getLine(rgchBuf,MAXBUF)) {
if (feof(file))
makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
makeError(line,CANT_READ_FILE);
}
}
}
*s = '\0';
return(s);
}
// processEschIn - Handles Esch characters in Script File lines
//
// Scope: Global.
//
// Purpose:
// Inline file lines are handled for escape characters. If a line contains an
// escaped newline then append the next line to it.
//
// Input: buf - the command line to be processed for ESCH characters
//
// Errors/Warnings:
// SYNTAX_UNEXPECTED_TOKEN - The makefile cannot end without the in-line file
// ending.
// CANT_READ_FILE - When the makefile is unreadable.
//
// Assumes:
// If the newline is escaped the newline is last char in 'pGlobalbuf'. Safe
// to do so because we got 'pGlobalBuf' via fgets(). ????
//
// Modifies Globals:
// line - if newline was Escaped update line
// file - the makefile being processed
// buf - gets next line appended if newline was Escaped (indirectly)
//
// Uses Globals:
// buf - Indirectly
void
processEschIn(
char *pGlobalBuf
)
{
char *p, *q;
p = pGlobalBuf;
while (p = _tcschr(p, '\n')) {
if (p > pGlobalBuf) {
char * pprev = _tcsdec(pGlobalBuf, p);
if (*pprev != ESCH) {
break;
}
}
p++;
if (!(q = fgets(p, (int)(size_t) (MAXBUF - (p - pGlobalBuf)), file))) {
if (feof(file)) {
makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
}
makeError(line, CANT_READ_FILE);
}
line++;
}
}
// parseInlineFileList - Parses file list and makes list of Inline files
//
// Scope: Global.
//
// Purpose:
// To handle multiple inline files, the names of the files are to be stored
// in a list. This function creates the list by parsing the command file
//
// Input: buf - the line to be parsed
//
// Modifies Globals:
// scriptFileList -- the list of script files.
void
parseInlineFileList(
char *buf
)
{
char *token;
processEschIn(buf);
token = nextInlineFile(&buf); //next inline file
while (token != NULL) {
SCRIPTLIST *newScript;
newScript = makeNewScriptListElement();
newScript->sFile = makeString(token);
appendScript(&scriptFileList, newScript);
token = nextInlineFile(&buf); // next inline file
}
}
// appendScript -- appends an element to the tail of a scriptlist
//
// Purpose:
// Traverse to the end of the list and append element there.
//
// Input:
// list -- the list to append to
// element -- the element inserted
//
// Modifies:
// the global list
void
appendScript(
SCRIPTLIST **list,
SCRIPTLIST *element
)
{
for (; *list; list = &(*list)->next)
;
*list = element;
}
char tok[MAXNAME];
// Space not included in the following macro as it is now a valid
// character for filenames [DS 14966]
#define NAME_CHAR(c) (c) != '>' && (c) != '<' && \
(c) != '^' && (c) != ',' && (c) != '\t' && \
(c) != '\n'
// nextInlineFile - gets next Inline file name from command line
//
// Scope: Local.
//
// Purpose:
// The command line syntax is complex. This function returns the next Inline
// file in the command line part passed to it. As a side effect it changes the
// pointer to just after this inline file name.
//
// Input: str - address of the part of command line under consideration.
//
// Output: Returns the next inline filename.
//
// Modifies Globals:
// Global - How and why modified
//
// Uses Globals:
// tok - the address of this static array is returned.
char *
nextInlineFile(
char **str
)
{
char *t = tok, *pStr = *str;
BOOL fFound = FALSE; // '<<' not found
BOOL fQuoted = FALSE; // found '\"'
while (!fFound) {
if (!(pStr = _tcschr(pStr, '<'))) {
return(NULL);
}
if (*++pStr == '<') {
fFound = TRUE;
}
}
// Since '<<' has been found we definitely have another Inline File
pStr++;
while (*pStr && NAME_CHAR(*pStr)) {
if (*pStr == '\"') {
fQuoted = !fQuoted;
}
if (*pStr == ' ' && !fQuoted) {
break;
}
if (*pStr == '$' && pStr[1] == '(') {
*t = '$';
*++t = '(';
while (*++pStr != '\n' && *pStr != ')') {
*t++ = *pStr;
}
if (*pStr == '\n') {
break;
}
} else {
*t = *pStr;
++t; ++pStr;
}
}
*t = '\0';
*str = pStr;
return(tok);
}
// processInline - Brief description of the function
//
// Output: Returns ... TRUE if cmdline returned is expanded
BOOL
processInline(
char *szCmd,
char **szCmdLine,
STRINGLIST **pMacroList,
BOOL fDump
)
{
char *szInline, *szUnexpInline; // Inline name, unexpanded
char *pCmdLine; // The executable line
FILE *infile; // The inline file
char *begInBlock, *inBlock, *pInBlock; // inline block
char szTmp[MAXNAME + 2]; // add 2 to allow space for quotes
STRINGLIST *newString;
int iKeywordLen;
if (begInBlock = _tcschr(szCmd, '\n')) {
*begInBlock = '\0';
*szCmdLine = expandMacros(szCmd, pMacroList);
*begInBlock = '\n';
begInBlock++;
// if not expanded, allocate a copy
if (*szCmdLine == szCmd)
*szCmdLine = makeString(szCmd);
} else {
*szCmdLine = makeString(szCmd);
return(FALSE);
}
pCmdLine = *szCmdLine;
//expand macros in the inline file ...
pInBlock = inBlock = expandMacros(begInBlock, pMacroList);
while (szUnexpInline = nextInlineFile(&pCmdLine)) {
BOOL fKeep = FALSE; // default is NOKEEP
char *newline;
// CAVIAR 3410 -- the inline filename has already been expaned
// by the time we get here... we just need to dup the name
// so that it is preserved long enough to delete it later... [rm]
//
// szInline = removeMacros(szUnexpInline);
szInline = makeString(szUnexpInline);
if (!*szInline) {
char *nmTmp;
if ((nmTmp = getenv("TMP")) != NULL && *nmTmp) {
assert(_tcslen(nmTmp) <= MAXNAME);
_tcsncpy(szTmp, nmTmp, MAXNAME);
} else
szTmp[0] = '\0';
if (!(infile = createDosTmp(szTmp)))
makeError(line, CANT_MAKE_INLINE, szTmp);
if (_tcschr(szTmp, ' ') && !_tcschr(szTmp, '"')) {
// if the filename (str) contains spaces
// and is unquoted, quote it, so that we can
// feed it properly to the command interpreter! [VS98 1931]
size_t size = _tcslen(szTmp);
memmove(szTmp+1, szTmp, size);
*szTmp = '"';
*(szTmp + size + 1) = '"';
*(szTmp + size + 2) = '\0';
}
replaceLtLt(szCmdLine, szTmp);
FREE(szInline);
szInline = makeString(szTmp);
} else if (!(infile = FILEOPEN(szInline, "w")))
makeError(line, CANT_MAKE_INLINE, szInline);
else
delInlineSymbol(*szCmdLine);
pCmdLine = *szCmdLine; // Because szCmdLine changed
createInline(infile, szInline, &pInBlock, fDump);
// Add handling of KEEP and NOKEEP here
// iKeywordLen is length of word after << on that line
newline = _tcschr(pInBlock , '\n');
iKeywordLen = newline ? ((int) (newline - pInBlock)) : _tcslen(pInBlock);
if (iKeywordLen > 3 && !_tcsnicmp(pInBlock, "keep", 4)) {
pInBlock +=4;
fKeep = (BOOL)TRUE;
} else if (iKeywordLen > 5 && !_tcsnicmp(pInBlock, "nokeep", 6))
pInBlock += 6;
else if (iKeywordLen)
makeError(line, SYNTAX_KEEP_INLINE_FILE);
if (*pInBlock == '\n')
pInBlock++;
fclose(infile);
// Add the file to list to be deleted; except for "KEEP"
if (!fKeep) {
newString = makeNewStrListElement();
newString->text = makeString(szInline);
appendItem(&delList, newString);
}
FREE(szInline);
}
if (inBlock != begInBlock)
FREE(inBlock);
return(TRUE);
}
void
replaceLtLt(
char **source,
char *str
)
{
char *szBuf;
char *p, *q;
// Don't subtract two for the << and forget to add 1 for the null termination.
szBuf = (char *) _alloca(_tcslen(*source) - 1 + _tcslen(str));
for (p = *source, q = szBuf;;++p,++q)
if (*p != '<')
*q = *p;
else if (*(p+1) != '<') {
*q = '<';
} else {
*q = '\0';
_tcscat(_tcscat(szBuf, str), p+2);
*source = (char *) REALLOC(*source, _tcslen(szBuf) + 1);
if (*source == NULL) {
makeError(0, OUT_OF_MEMORY);
}
_tcscpy(*source, szBuf);
break;
}
}
void
createInline(
FILE *file,
const char *szFName,
char **szString,
BOOL fDump
)
{
char *t, *u;
BOOL fFirstLine = TRUE;
while (t = _tcschr(*szString, '\n'))
if (!_tcsncmp(*szString, "<<", 2)) {
*szString += 2;
break;
} else {
// [msdev96 #3036]
// "nmake /n" should somehow show the contents of
// response files (esp. temp ones that are deleted
// right after use). In order to preserve the batch
// file format of the output (at least in common
// cases), we use a syntax like
// "echo. command >> resp_file" (the dot after
// the "echo" command is useful for echo'ing
// empty strings.)
//
// A new switch has been added for this
// purpose ("nmake /u" dumps inline files)
if (fDump) {
*t = '\0';
echoLine(*szString, szFName, !fFirstLine);
*t = '\n';
}
for (u = *szString; u <= t; u++)
fputc(*u, file);
*szString = u;
fFirstLine = FALSE;
}
if (!t && !_tcsncmp(*szString, "<<", 2))
*szString += 2;
}
// echoLine
//
// Usage: echoLine (szLine, szFName, fAppend)
//
// Description:
// prints an "echo szLine >> szFName"-like command
// uses ">>" if fAppend is TRUE, ">" otherwise
void
echoLine(char *szLine, const char *szFName, BOOL fAppend)
{
// use a 1024-byte buffer to split long lines, so that "echo"
// commands can be handled by the command interpreter
static char buf[1024];
BOOL fBlankLine = TRUE;
char *pch;
char *szCur = szLine;
size_t cbBuf;
for (pch = szLine; *pch; pch = _tcsinc (pch)) {
if (!_istspace((unsigned char)*pch)) {
fBlankLine = FALSE;
break;
}
}
if (fBlankLine) {
printf("\techo. %s %s\n",
fAppend ? ">>" : ">",
szFName);
return;
}
// calculate available buffer length for szLine
// assuming space for "\techo. ", " >> " and szFName
cbBuf = sizeof(buf) - 11 - _tcslen( szFName ) - 1;
while (*szCur) {
size_t iLast;
_tcsncpy (buf, szCur, cbBuf);
iLast = _tcslen (buf);
if (cbBuf < _tcslen (szCur)) {
// find index of character next to the
// last occurence of white space in buffer
for (pch = buf; *pch; pch = _tcsinc(pch)) {
if (_istspace((unsigned char)*pch)) {
iLast = (size_t) (pch - buf + 1);
}
}
}
buf[iLast] = 0;
printf("\techo %s %s %s\n",
buf,
fAppend ? ">>" : ">",
szFName);
szCur += iLast;
fAppend = TRUE;
}
}
void
delInlineSymbol(
char *s
)
{
char *p = _tcschr(s, '<');
while (p[1] != '<')
p = _tcschr(p+1, '<');
// "<<" found
_tcscpy(p, p+2);
}
// getLine - get next line processing NMAKE conditionals enroute
//
// Scope: Local
//
// Purpose:
// This function handles directives in inline files. This function gets the
// next line of input ... managing conditionals on the way.
//
// Input:
// pchLine - pointer to buffer where line is copied
// n - size of buffer
//
// Output:
// Returns ... NULL, on EOF
// ... non-zero on success
//
// Uses Globals:
// line - lexer's line count
// colZero - if starting from colZero, needed by lgetc()
//
// Notes:
// Similar to fgets() without stream
//
// Implementation Notes:
// lgetc() handles directives while getting the next character. It handles
// directives when the global colZero is TRUE.
char *
getLine(
char *pchLine,
int n
)
{
char *end = pchLine + n;
int c;
while (c = lgetc()) {
switch (c) {
case EOF:
*pchLine = '\0';
return(NULL);
default:
*pchLine++ = (char)c;
break;
}
if (pchLine == end) {
pchLine[-1] = '\0';
UngetTxtChr(c, file);
return(pchLine);
} else if (c == '\n') {
colZero = TRUE;
++line;
*pchLine = '\0';
return(pchLine);
} else
colZero = FALSE; // the last character was not a '\n' and
// we are not at the beginning of the file
}
return(pchLine);
}