667 lines
16 KiB
C
667 lines
16 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:
|
|
* 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:
|
|
* 1> This file contains '#ifdef NOESC' to segregate text which uses ESCH, the
|
|
* NMAKE escape character.
|
|
* 2> Sections with 'NOTE:' inside comments marks important/incomplete items.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
// NOTE: Function headers yet to be completed; other comments are incomplete
|
|
|
|
|
|
/* Include Files */
|
|
|
|
#include "nmake.h"
|
|
#include "nmmsg.h"
|
|
#include "proto.h"
|
|
#include "globals.h"
|
|
#include "grammar.h"
|
|
|
|
|
|
/* L O C A L Function Prototypes */
|
|
|
|
LOCAL void NEAR processEschIn(char *);
|
|
// NOTE: This may go soon (use nextInlineFile ?)
|
|
LOCAL void NEAR parseInlineFileList(char *);
|
|
// NOTE: The next one has to go soon
|
|
LOCAL void NEAR appendScript(SCRIPTLIST**,SCRIPTLIST*);
|
|
LOCAL void NEAR delInlineSymbol(char*);
|
|
LOCAL char * NEAR nextInlineFile(char **);
|
|
|
|
#ifdef DEAD_CODE
|
|
LOCAL BOOL NEAR duplicateInline(char *);
|
|
#endif
|
|
// NOTE: Probably needs a new name
|
|
LOCAL void NEAR replaceLtLt(char **, char *);
|
|
LOCAL void NEAR createInline(FILE *, char **);
|
|
LOCAL char * NEAR getLine(char *, int);
|
|
|
|
|
|
/* E X T E R N Function Prototypes */
|
|
|
|
// NOTE: delScriptFiles() from nmake.c not yet brought in here
|
|
extern FILE * NEAR createDosTmp(char *);
|
|
|
|
|
|
/* G L O B A L Function Prototypes */
|
|
|
|
char * NEAR makeInlineFiles(char *, char **, char **);
|
|
BOOL NEAR processInline(char *, char **, STRINGLIST **);
|
|
|
|
/*** 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.
|
|
*
|
|
* Assumes:
|
|
* Modifies Globals:
|
|
* Uses Globals:
|
|
* file - global stream
|
|
* line - lexer's line count
|
|
*
|
|
* Notes:
|
|
* Usage notes and other important notes
|
|
*
|
|
*******************************************************************************/
|
|
char * NEAR
|
|
makeInlineFiles(s, begin, end)
|
|
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;
|
|
|
|
_ftcscpy(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 = allocate(MAXBUF<<1);
|
|
_ftcsncpy(szTmpBuf,*begin,MAXBUF);
|
|
s = szTmpBuf + MAXBUF;
|
|
size = MAXBUF << 1;
|
|
*end = szTmpBuf + size;
|
|
}
|
|
else {
|
|
if ((size + MAXBUF < size) /* overflow error */
|
|
|| !(szTmpBuf = 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)) {
|
|
#ifdef INLINE
|
|
if (!fgets(rgchBuf,MAXBUF)) {
|
|
#endif
|
|
if (feof(file))
|
|
makeError(line,SYNTAX_UNEXPECTED_TOKEN,"EOF");
|
|
makeError(line,CANT_READ_FILE);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
fPastCmd = TRUE;
|
|
if (!getLine(rgchBuf,MAXBUF)) {
|
|
#ifdef INLINE
|
|
if (!fgets(rgchBuf,MAXBUF,file)) {
|
|
#endif
|
|
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
|
|
*
|
|
* Output:
|
|
*
|
|
* 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
|
|
*
|
|
* Notes:
|
|
*
|
|
*******************************************************************************/
|
|
#ifndef NOESCH
|
|
LOCAL void NEAR
|
|
processEschIn(pGlobalBuf)
|
|
char *pGlobalBuf;
|
|
{
|
|
char *p, *q;
|
|
|
|
p = pGlobalBuf;
|
|
while (p = _ftcschr(p, '\n')) {
|
|
if (p[-1] == ESCH) {
|
|
++p;
|
|
if (!(q = fgets(p, MAXBUF - (p - pGlobalBuf), file))) {
|
|
if (feof(file))
|
|
makeError(line, SYNTAX_UNEXPECTED_TOKEN, "EOF");
|
|
makeError(line, CANT_READ_FILE);
|
|
}
|
|
line++;
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*** 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
|
|
*
|
|
* Output:
|
|
*
|
|
* Errors/Warnings:
|
|
*
|
|
* Assumes:
|
|
*
|
|
* Modifies Globals:
|
|
* scriptFileList -- the list of script files.
|
|
*
|
|
* Uses Globals:
|
|
*
|
|
* Notes:
|
|
*
|
|
**********************************************************************/
|
|
void NEAR
|
|
parseInlineFileList(buf)
|
|
char *buf;
|
|
{
|
|
SCRIPTLIST *new;
|
|
char *token;
|
|
|
|
processEschIn(buf);
|
|
token = nextInlineFile(&buf); //next inline file
|
|
for (;;) {
|
|
if (!token) break;
|
|
new = makeNewScriptListElement();
|
|
new->sFile = makeString(token);
|
|
appendScript(&scriptFileList, new);
|
|
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 NEAR
|
|
appendScript(list, element)
|
|
SCRIPTLIST **list;
|
|
SCRIPTLIST *element;
|
|
{
|
|
for (; *list; list = &(*list)->next);
|
|
*list = element;
|
|
}
|
|
|
|
char tok[MAXNAME];
|
|
|
|
#define NAME_CHAR(c) (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.
|
|
*
|
|
* Errors/Warnings:
|
|
*
|
|
* Assumes:
|
|
*
|
|
* Modifies Globals:
|
|
* Global - How and why modified
|
|
*
|
|
* Uses Globals:
|
|
* tok - the address of this static array is returned.
|
|
*
|
|
* Notes:
|
|
*
|
|
*******************************************************************************/
|
|
LOCAL char * NEAR
|
|
nextInlineFile(str)
|
|
char **str;
|
|
{
|
|
char *t = tok,
|
|
*pStr = *str;
|
|
BOOL fFound = FALSE; // '<<' not found
|
|
|
|
while (!fFound)
|
|
if (!(pStr = _ftcschr(pStr, '<')))
|
|
return(NULL);
|
|
else if (*++pStr == '<')
|
|
fFound = TRUE;
|
|
|
|
//Since '<<' has been found we definitely have another Inline File
|
|
pStr++;
|
|
while (*pStr && NAME_CHAR(*pStr)) {
|
|
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);
|
|
}
|
|
|
|
/*** duplicateInline -- check if inline filename is already present ************
|
|
*
|
|
* Scope:
|
|
* Local.
|
|
*
|
|
* Purpose:
|
|
* Checks if an inline filename has been used or not
|
|
*
|
|
* Input:
|
|
* name - the name to check for
|
|
*
|
|
* Output:
|
|
* Returns ... TRUE if it is a duplicate inline file and FALSE if not.
|
|
*
|
|
* Errors/Warnings:
|
|
*
|
|
* Assumes:
|
|
*
|
|
* Modifies Globals:
|
|
*
|
|
* Uses Globals:
|
|
* inlineFileList - The global list of inline files
|
|
*
|
|
* Notes:
|
|
*
|
|
*******************************************************************************/
|
|
#ifdef DEAD_CODE
|
|
BOOL NEAR
|
|
duplicateInline(name)
|
|
char *name;
|
|
{
|
|
BOOL fFound = FALSE;
|
|
STRINGLIST *pList = inlineFileList;
|
|
|
|
for (; pList && !fFound; pList = pList->next) {
|
|
if (!_ftcsnicmp(name, pList->text, _ftcslen(pList->text)))
|
|
fFound = TRUE;
|
|
}
|
|
return(fFound);
|
|
}
|
|
#endif
|
|
|
|
/*** processInline - Brief description of the function *************************
|
|
*
|
|
* Scope:
|
|
* Global.
|
|
*
|
|
* Purpose:
|
|
* What does the function do?
|
|
*
|
|
* Input:
|
|
* parameter - description
|
|
*
|
|
* Output:
|
|
* Returns ... TRUE if cmdline returned is expanded
|
|
*
|
|
* Errors/Warnings:
|
|
* error/warning - Why?
|
|
*
|
|
* Assumes:
|
|
* Whatever it assumes
|
|
*
|
|
* Modifies Globals:
|
|
* Global - How and why modified
|
|
*
|
|
* Uses Globals:
|
|
* Global - How and why used
|
|
*
|
|
* Notes:
|
|
* Usage notes and other important notes
|
|
*
|
|
*******************************************************************************/
|
|
BOOL NEAR
|
|
processInline(
|
|
char *szCmd,
|
|
char **szCmdLine,
|
|
STRINGLIST **pMacroList
|
|
) {
|
|
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];
|
|
STRINGLIST *new;
|
|
int iKeywordLen;
|
|
|
|
if (begInBlock = _ftcschr(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(_ftcslen(nmTmp) <= MAXNAME);
|
|
_ftcsncpy(szTmp, nmTmp, MAXNAME);
|
|
}
|
|
else
|
|
szTmp[0] = '\0';
|
|
if (!(infile = createDosTmp(szTmp)))
|
|
makeError(line, CANT_MAKE_INLINE, szTmp);
|
|
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, &pInBlock);
|
|
|
|
//Add handling of KEEP and NOKEEP here
|
|
//iKeywordLen is length of word after << on that line
|
|
newline = _ftcschr(pInBlock , '\n');
|
|
iKeywordLen = newline ? newline - pInBlock : _ftcslen(pInBlock);
|
|
if (iKeywordLen > 3 && !_ftcsnicmp(pInBlock, "keep", 4)) {
|
|
pInBlock +=4;
|
|
fKeep = (BOOL)TRUE;
|
|
}
|
|
else if (iKeywordLen > 5 && !_ftcsnicmp(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) {
|
|
new = makeNewStrListElement();
|
|
new->text = makeString(szInline);
|
|
appendItem(&delList, new);
|
|
}
|
|
FREE(szInline);
|
|
}
|
|
if (inBlock != begInBlock)
|
|
FREE(inBlock);
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
void NEAR
|
|
replaceLtLt(source, str)
|
|
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 = _alloca(_ftcslen(*source) - 1 + _ftcslen(str));
|
|
for (p = *source, q = szBuf;;++p,++q)
|
|
if (*p != '<')
|
|
*q = *p;
|
|
else if (*(p+1) != '<') {
|
|
*q = '<';
|
|
}
|
|
else {
|
|
*q = '\0';
|
|
_ftcscat(_ftcscat(szBuf, str), p+2);
|
|
*source = (char *)REALLOC(*source, _ftcslen(szBuf) + 1);
|
|
_ftcscpy(*source, szBuf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void NEAR
|
|
createInline(file, szString)
|
|
FILE *file;
|
|
char **szString;
|
|
{
|
|
char *t, *u;
|
|
|
|
while (t = _ftcschr(*szString, '\n'))
|
|
if (!_ftcsncmp(*szString, "<<", 2)) {
|
|
*szString += 2;
|
|
break;
|
|
}
|
|
else {
|
|
for (u = *szString; u <= t; u++)
|
|
fputc(*u, file);
|
|
*szString = u;
|
|
}
|
|
if (!t && !_ftcsncmp(*szString, "<<", 2))
|
|
*szString += 2;
|
|
}
|
|
|
|
LOCAL void NEAR
|
|
delInlineSymbol(s)
|
|
char *s;
|
|
{
|
|
char *p = _ftcschr(s, '<');
|
|
while (p[1] != '<')
|
|
p = _ftcschr(p+1, '<');
|
|
// "<<" found
|
|
_ftcscpy(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
|
|
*
|
|
* Errors/Warnings:
|
|
* Assumes:
|
|
* Modifies Globals:
|
|
* 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.
|
|
*
|
|
*****************************************************************************/
|
|
LOCAL char * NEAR
|
|
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
|
|
}
|
|
}
|