|
|
// 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 { void *pv; if ((size + MAXBUF < size) || !(pv = REALLOC(szTmpBuf,size+MAXBUF))) { makeError(line, MACRO_TOO_LONG); } else { szTmpBuf = (char *) pv; } 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) { 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; void *pv;
// Don't subtract two for the << and forget to add 1 for the null termination.
szBuf = (char *) malloc(_tcslen(*source) - 1 + _tcslen(str)); if (!szBuf) { makeError(0, OUT_OF_MEMORY); } 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); pv = REALLOC(*source, _tcslen(szBuf) + 1); if (pv) { *source = (char *) pv; } else { makeError(0, OUT_OF_MEMORY); } _tcscpy(*source, szBuf); break; }
free(szBuf); }
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 L_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(L_buf) - 11 - _tcslen( szFName ) - 1;
while (*szCur) { size_t iLast; _tcsncpy (L_buf, szCur, cbBuf); iLast = _tcslen (L_buf); if (cbBuf < _tcslen (szCur)) { // find index of character next to the
// last occurence of white space in buffer
for (pch = L_buf; *pch; pch = _tcsinc(pch)) { if (_istspace((unsigned char)*pch)) { iLast = (size_t) (pch - L_buf + 1); } } }
L_buf[iLast] = 0; printf("\techo %s %s %s\n", L_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); }
|