1019 lines
29 KiB
C
1019 lines
29 KiB
C
/*** ACTION.C -- routines called by the parser *******************************
|
||
*
|
||
*
|
||
* Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
|
||
*
|
||
* Purpose:
|
||
* This module contains the routines called during parsing of a makefile.
|
||
*
|
||
* Revision History:
|
||
* 15-Oct-1993 HV Use tchar.h instead of mbstring.h directly, change STR*() to _ftcs*()
|
||
* 10-May-1993 HV Add include file mbstring.h
|
||
* Change the str* functions to STR*
|
||
* 13-Feb-1990 SB nextComponent() missed out mixed case of quoted/non-quote list
|
||
* 02-Feb-1990 SB Add nextComponent() for Longfilename handling
|
||
* 08-Dec-1989 SB removed local used without initialization warnings for -Oes
|
||
* 07-Dec-1989 SB removed register to compile using C6 -Oes (warning gone)
|
||
* 06-Dec-1989 SB changed expandFileNames() type to void instead of void *
|
||
* 22-Nov-1989 SB Changed free() to FREE()
|
||
* 13-Nov-1989 SB Removed an unreferenced local in endNameList()
|
||
* 02-Oct-1989 SB add dynamic inline file handling support
|
||
* 04-Sep-1989 SB Add A_DEPENDENT and fix macro inheritance
|
||
* 24-Aug-1989 SB Allow $* on dependency lines
|
||
* 14-Jul-1989 SB Environment macro was getting updated even when CMDLINE
|
||
* macro was present
|
||
* 29-Jun-1989 SB addItemToList() now maintains Global inline file List
|
||
* 26-Jun-1989 SB Fixed -e for recursive NMAKE
|
||
* 22-May-1989 SB NZ options work independently. -NZ does both stuff now
|
||
* 13-May-1989 SB Changed delList to contain just names of files and no "del "
|
||
* 14-Apr-1989 SB made targetList NEAR, no 'del inlinefile' cmd for -n now
|
||
* 05-Apr-1989 SB Added parameters to makeRule() & makeTarget(); this get rid
|
||
* of globals
|
||
* 03-Apr-1989 SB changed all functions to NEAR to get them into one module
|
||
* 21-Mar-1989 SB changed assignDependents() and assignCommands() to handle
|
||
* multiple target case correctly.
|
||
* 20-Mar-1989 SB startNamelist() doesn't flag error if target macro is null
|
||
* and >1 target given; commented startNameList()
|
||
* 16-Feb-1989 SB addItemToList() now appends to delList instead of List so
|
||
* that all 'del scriptFile' cmds can be at the end of the make
|
||
* 29-Jan-1989 SB added targList but not used as yet
|
||
* 19-Jan-1989 SB changed startNameList() to avoid GP fault, bug# 162
|
||
* 18-Jan-1989 SB modified endNameList(), added makeList() and makeBuildList()
|
||
* and added a parameter to makeTarget() for bug# 161
|
||
* 21-Dec-1988 SB use scriptFileList to handle multiple scriptFiles
|
||
* Improve KEEP/NOKEEP;each file can have its own action
|
||
* 16-Dec-1988 SB addItemToList() is now equipped for KEEP/NOKEEP
|
||
* 14-Dec-1988 SB addItemToList() modified for 'Z' option -- adds a delete
|
||
* command for deleting temporary script files
|
||
* 5-Nov-1988 RB Fixed macro inheritance for recursive definitions.
|
||
* 27-Oct-1988 SB put malloc for allocate in putEnvStr() -- error checking
|
||
* 23-Oct-1988 SB Using putEnvStr() for putenv() to simplify code
|
||
* 21-Oct-1988 SB Added fInheritUserEnv flag modifications to makeMacro()
|
||
* and putMacro() for macro inheritance
|
||
* 19-Sep-1988 RB Remove warning for MAKE redefinition.
|
||
* 22-Aug-1988 RB Clean up for !UNDEF'ed macros.
|
||
* 17-Aug-1988 RB Clean up.
|
||
* 14-Jul-1988 rj Added initialization of dateTime field of BUILDBLOCKs.
|
||
* 7-Jul-1988 rj Added targetFlag parameter to hash() calls.
|
||
* Fixed bug: redefined macros didn't get flags reset.
|
||
* 09-May-1988 rb Don't swallow no-match wildcards.
|
||
*
|
||
******************************************************************************/
|
||
|
||
#include "nmake.h"
|
||
#include "nmmsg.h"
|
||
#include "proto.h"
|
||
#include "globals.h"
|
||
#include "grammar.h"
|
||
|
||
LOCAL void NEAR startNameList(void);
|
||
LOCAL void NEAR makeRule(STRINGLIST *);
|
||
LOCAL void NEAR makeTarget(char*, BOOL, BUILDBLOCK**);
|
||
LOCAL void NEAR appendPseudoTargetList(STRINGLIST**, STRINGLIST*);
|
||
LOCAL void NEAR clearSuffixes(void);
|
||
LOCAL BOOL NEAR doSpecial(char*);
|
||
LOCAL BUILDLIST * NEAR makeBuildList(BUILDBLOCK *);
|
||
STRINGLIST * NEAR makeList(STRINGLIST *);
|
||
LOCAL char * NEAR nextComponent(char **);
|
||
|
||
/* Globals in this module */
|
||
|
||
/* created by endNameList() & freed by assignBuildCommands(). In use because *
|
||
* although global 'list' has this list is used by too many routines and the *
|
||
* complete sequence of actions on 'list' is unknown */
|
||
|
||
STRINGLIST * NEAR targetList; //corr to a dependency block
|
||
|
||
|
||
/*** makeName -- create copy of a name seen by lexer ***************************
|
||
*
|
||
* Purpose:
|
||
* Create a copy of a macro or 1st name in a target/dependency list. It also
|
||
* does the groundwork for expansion of macros in name and saves values.
|
||
*
|
||
* Input:
|
||
*
|
||
* Output:
|
||
*
|
||
* Assumes:
|
||
* That the lexical routines save the token in buf
|
||
*
|
||
* Modifies Globals:
|
||
* name -- the pointer to the copy created, gets allocated memory
|
||
* macros -- the list of macro values in name. Used later by startNameList()
|
||
* to expand macros in name and get expanded target name.
|
||
*
|
||
* Uses Globals:
|
||
* buf -- the lexical routines return a token in this
|
||
*
|
||
* Notes:
|
||
* The token in buf could be part of a macrodefinition or a target list. The
|
||
* next token determines if it is a macrodefn or a targetlist we are parsing.
|
||
* The next token would overwrite the current token and so it is saved in name.
|
||
*
|
||
*******************************************************************************/
|
||
void NEAR
|
||
makeName()
|
||
{
|
||
findMacroValues(buf, ¯os, NULL, NULL, 0, 0, 0);
|
||
name = makeString(buf);
|
||
}
|
||
|
||
/*
|
||
* don't expand build lines for rules now -- expand after everything read in
|
||
* that way CC and CFLAGS used in rules from tools.ini but not defined until
|
||
* the makefile will have appropriate values. Redefining macros for use
|
||
* in targets w/o explicit build commands doesn't work. Macros in rules have
|
||
* the value of their last definition in the makefile.
|
||
*/
|
||
|
||
void NEAR
|
||
addItemToList()
|
||
{
|
||
STRINGLIST *p; /* from lexer */
|
||
STRINGLIST *new;
|
||
|
||
if (name) {
|
||
SET(actionFlags, A_TARGET);
|
||
startNameList();
|
||
name = NULL;
|
||
}
|
||
if (ON(actionFlags, A_TARGET)) {
|
||
if (isRule(buf)) {
|
||
if (ON(actionFlags, A_RULE))
|
||
makeError(currentLine, TOO_MANY_RULE_NAMES);
|
||
makeError(currentLine, MIXED_RULES);
|
||
}
|
||
}
|
||
p = makeNewStrListElement();
|
||
if (ON(actionFlags, A_STRING)) { /* we collect macros */
|
||
p->text = string; /* for dependents & */
|
||
string = NULL; /* build lines for */
|
||
} /* non-implicit rules*/
|
||
else p->text = makeString(buf);
|
||
new = p; /* build lines for */
|
||
|
||
if (OFF(actionFlags, A_RULE) /* rules get expanded*/
|
||
|| ON(actionFlags, A_TARGET)) { /* after entire make-*/
|
||
findMacroValues(p->text, ¯os, NULL, NULL, 0, 0, 0); /* file parsed */
|
||
}
|
||
if (ON(actionFlags, A_TARGET)) {
|
||
p = macros;
|
||
expandFileNames("$", &new, ¯os);
|
||
expandFileNames("*?", &new, NULL);
|
||
while (macros = p) {
|
||
p = p->next;
|
||
FREE_STRINGLIST(macros);
|
||
}
|
||
}
|
||
appendItem(&list, new);
|
||
}
|
||
|
||
/*** startNameList -- puts in the first element into list **********************
|
||
*
|
||
* Scope:
|
||
* Local.
|
||
*
|
||
* Purpose:
|
||
* Puts in the first name seen into a list
|
||
*
|
||
* Input:
|
||
*
|
||
* Output:
|
||
*
|
||
* Errors/Warnings:
|
||
* TARGET_MACRO_IS_NULL -- if the macro used as a target expands to null
|
||
*
|
||
* Assumes:
|
||
* The global 'list' is originally a null list, & the global 'macro' points to
|
||
* a list of values used for expanding the macros in global 'name'.
|
||
*
|
||
* Modifies Globals:
|
||
* list -- the list of names; set to contain the first name here or to a
|
||
* list of values if 'name' contains a macro invocation.
|
||
* macros -- the list of values reqd for macro expansion; the list is
|
||
* freed and macros is made NULL
|
||
* currentFlags -- the flags for current target; set to global flags
|
||
* actionFlags -- determine actions to be done; if the name is a rule then set
|
||
* the rule bit
|
||
*
|
||
* Uses Globals:
|
||
* name -- the first name seen in a list of names.
|
||
* flags -- the global flags setup by the options specified
|
||
* actionFlags -- if handling targets then no error as we have > 1 target
|
||
*
|
||
* Notes:
|
||
* If there is more than one target then actionFlags has A_TARGET flag set and
|
||
* startNameList() is called from addItemToList. In this case don't flag error.
|
||
*
|
||
*******************************************************************************/
|
||
LOCAL void NEAR
|
||
startNameList()
|
||
{
|
||
STRINGLIST *p;
|
||
|
||
currentFlags = flags; //set flags for cur target
|
||
p = makeNewStrListElement();
|
||
p->text = name;
|
||
list = p; //list contains name
|
||
p = macros;
|
||
expandFileNames("$", &list, ¯os); //expand macros in name
|
||
expandFileNames("*?", &list, NULL); //expand wildcards
|
||
while (macros = p) { // free macro list
|
||
p = p->next;
|
||
FREE_STRINGLIST(macros);
|
||
}
|
||
if (!list && OFF(actionFlags, A_TARGET))
|
||
makeError(line, TARGET_MACRO_IS_NULL, name); //target null & 1 target
|
||
if (list && isRule(list->text))
|
||
SET(actionFlags, A_RULE);
|
||
}
|
||
|
||
|
||
/*** endNameList -- semantic actions when a list is fully seen *****************
|
||
*
|
||
* Purpose:
|
||
* When the parser has seen an entire list then it needs to do some semantic
|
||
* actions. It calls endNameList() to do these actions. The action depends on
|
||
* the values in certain globals.
|
||
*
|
||
* Input:
|
||
*
|
||
* Output:
|
||
*
|
||
* Assumes:
|
||
* Whatever it assumes
|
||
*
|
||
* Errors/Warnings:
|
||
*
|
||
* Modifies Globals:
|
||
* actionFlags --
|
||
*
|
||
* Uses Globals:
|
||
* name -- The first element seen (if non null)
|
||
* actionFlags -- The flag determining semantic & data structure actions
|
||
* buf -- The delimiter seen after list
|
||
* list -- The list of elements seen
|
||
*
|
||
* Notes:
|
||
* explanations/comments etc
|
||
*
|
||
*******************************************************************************/
|
||
void NEAR
|
||
endNameList()
|
||
{
|
||
if (name) { /* if only one name to left of : */
|
||
startNameList(); /* it hasn't been put in list yet*/
|
||
name = NULL;
|
||
}
|
||
else CLEAR(actionFlags, A_TARGET); /* clear target flag */
|
||
if (buf[1]) SET(currentFlags, F2_DOUBLECOLON); /* so addItemToList()*/
|
||
if (!list) /* won't expand names*/
|
||
makeError(currentLine, SYNTAX_NO_TARGET_NAME); /* of dependents */
|
||
if (ON(actionFlags, A_RULE)) {
|
||
if (ON(currentFlags, F2_DOUBLECOLON))
|
||
makeError(currentLine, BAD_SEPARATOR);
|
||
makeRule(list);
|
||
FREE_STRINGLIST(list);
|
||
}
|
||
else if (!(list->next) /* special pseudotarget ... */
|
||
&& doSpecial(list->text)) {
|
||
FREE(list->text); /* don't need ".SUFFIXES" etc */
|
||
FREE_STRINGLIST(list);
|
||
}
|
||
else /* regular target */
|
||
targetList = list;
|
||
list = NULL;
|
||
//We are now looking for a dependent
|
||
SET(actionFlags, A_DEPENDENT);
|
||
}
|
||
|
||
LOCAL BOOL NEAR
|
||
doSpecial(s)
|
||
char *s;
|
||
{
|
||
BOOL status = FALSE;
|
||
|
||
if (!_ftcsicmp(s, silent)) {
|
||
SET(actionFlags, A_SILENT);
|
||
setFlags('s', TRUE);
|
||
status = TRUE;
|
||
}
|
||
if (!_ftcsicmp(s, ignore)) {
|
||
SET(actionFlags, A_IGNORE);
|
||
setFlags('i', TRUE);
|
||
status = TRUE;
|
||
}
|
||
else if (!_ftcscmp(s, suffixes)) {
|
||
SET(actionFlags, A_SUFFIX);
|
||
status = TRUE;
|
||
}
|
||
else if (!_ftcscmp(s, precious)) {
|
||
SET(actionFlags, A_PRECIOUS);
|
||
status = TRUE;
|
||
}
|
||
return(status);
|
||
}
|
||
|
||
void NEAR
|
||
expandFileNames(string, sourceList, macroList)
|
||
char *string;
|
||
STRINGLIST **sourceList;
|
||
STRINGLIST **macroList;
|
||
{
|
||
char *s,
|
||
*t = NULL;
|
||
STRINGLIST *p; /* Main list pointer */
|
||
STRINGLIST *pNew, /* Pointer to new list */
|
||
*pBack; /* Pointer to one element back */
|
||
char *saveText = NULL;
|
||
|
||
for (pBack = NULL, p = *sourceList; p;) {
|
||
/*
|
||
* If no expand-character is found, continue to next list element.
|
||
*/
|
||
if (!_ftcspbrk(p->text, string)) {
|
||
pBack = p;
|
||
p = pBack->next;
|
||
continue;
|
||
}
|
||
/*
|
||
* Either expand macros or wildcards.
|
||
*/
|
||
if (*string == '$') {
|
||
t = expandMacros(p->text, macroList);
|
||
FREE(p->text);
|
||
}
|
||
else {
|
||
/*
|
||
* If the wildcard string does not expand to anything, go to
|
||
* next list elment. Do not remove p from the original list
|
||
* else we must check for null elsewhere.
|
||
*/
|
||
|
||
// CAVIAR 3912 -- do not attempt to expand wildcards that
|
||
// occur in inference rules [rm]
|
||
|
||
if (isRule(p->text) || (pNew = expandWildCards(p->text)) == NULL) {
|
||
pBack = p;
|
||
p = pBack->next;
|
||
continue;
|
||
}
|
||
saveText = p->text;
|
||
}
|
||
/*
|
||
* At this point we have a list of expanded names to replace p with.
|
||
*/
|
||
if (pBack) {
|
||
pBack->next = p->next;
|
||
FREE_STRINGLIST(p);
|
||
p = pBack->next;
|
||
}
|
||
else {
|
||
*sourceList = p->next;
|
||
FREE_STRINGLIST(p);
|
||
p = *sourceList;
|
||
}
|
||
if (*string == '$') { /* if expanding macros*/
|
||
char *str = t;
|
||
if (s = nextComponent(&str)) {
|
||
do { /* put expanded names */
|
||
pNew = makeNewStrListElement(); /* at front of list */
|
||
pNew->text = makeString(s); /* so we won't try to*/
|
||
prependItem(sourceList, pNew); /* re-expand them */
|
||
if (!pBack) pBack = pNew;
|
||
} while (s = nextComponent(&str));
|
||
}
|
||
FREE(t);
|
||
continue;
|
||
}
|
||
else if (pNew) { /* if matches for * ? */
|
||
//BUGBUG : Wild cards within Quoted strings will fail
|
||
if (!pBack)
|
||
for (pBack = pNew; pBack->next; pBack = pBack->next);
|
||
appendItem(&pNew, *sourceList); /* put at front of old*/
|
||
*sourceList = pNew; /* list */
|
||
}
|
||
FREE(saveText);
|
||
}
|
||
}
|
||
/*** nextComponent - returns next component from expanded name *****************
|
||
*
|
||
* Scope:
|
||
* Local (used by expandFilenames)
|
||
*
|
||
* Purpose:
|
||
* Given a target string (target with macros expanded) this function returns a
|
||
* name component. Previously _ftcstok(s, " \t") was used but with the advent of
|
||
* quoted filenames this is no good.
|
||
*
|
||
* Input:
|
||
* szExpStr - the target name with macros expanded
|
||
*
|
||
* Output:
|
||
* Returns pointer to next Component; NULL means no more components left.
|
||
*
|
||
* Errors/Warnings:
|
||
*
|
||
* Assumes:
|
||
* That that two quoted strings are seperated by whitespace.
|
||
*
|
||
* Modifies Globals:
|
||
*
|
||
* Uses Globals:
|
||
*
|
||
* Notes:
|
||
*
|
||
*******************************************************************************/
|
||
|
||
char * NEAR
|
||
nextComponent(szExpStr)
|
||
char **szExpStr;
|
||
{
|
||
char *t, *next;
|
||
|
||
t = *szExpStr;
|
||
|
||
while (WHITESPACE(*t))
|
||
t++;
|
||
|
||
next = t;
|
||
if (!*t)
|
||
return(NULL);
|
||
|
||
if (*t == '"')
|
||
for (; *++t && *t != '"';)
|
||
;
|
||
else
|
||
for (; *t && *t != ' ' && *t != '\t'; t++)
|
||
;
|
||
|
||
if (WHITESPACE(*t))
|
||
*t = '\0';
|
||
else if (*t == '"') {
|
||
//Assume that quoted string is followed by Whitespace
|
||
t++;
|
||
*t = '\0';
|
||
}
|
||
//If at end of string then backup a byte so that next time we don't go past
|
||
else if (!*t)
|
||
t--;
|
||
*szExpStr = t+1;
|
||
return(next);
|
||
}
|
||
|
||
void NEAR
|
||
assignDependents() /* append dependents */
|
||
{ /* to existing ones */
|
||
/* (if any) */
|
||
char *which = NULL;
|
||
|
||
if (ON(actionFlags, A_DEPENDENT))
|
||
CLEAR(actionFlags, A_DEPENDENT);
|
||
if (ON(actionFlags, A_RULE)) {
|
||
if (list) makeError(currentLine, DEPENDENTS_ON_RULE);
|
||
}
|
||
else if (ON(actionFlags, A_SILENT)
|
||
|| ON(actionFlags, A_IGNORE)) {
|
||
if (list) {
|
||
if (ON(actionFlags, A_SILENT))
|
||
which = silent;
|
||
else if (ON(actionFlags, A_IGNORE))
|
||
which = ignore;
|
||
makeError(currentLine, DEPS_ON_PSEUDO, which);
|
||
}
|
||
}
|
||
else if (ON(actionFlags, A_SUFFIX)) {
|
||
if (!list)
|
||
clearSuffixes();
|
||
else
|
||
appendPseudoTargetList(&dotSuffixList, list);
|
||
}
|
||
else if (ON(actionFlags, A_PRECIOUS)) {
|
||
if (list)
|
||
appendPseudoTargetList(&dotPreciousList, list);
|
||
}
|
||
else {
|
||
block = makeNewBuildBlock();
|
||
block->dependents = list;
|
||
block->dependentMacros = macros;
|
||
}
|
||
list = NULL;
|
||
macros = NULL;
|
||
SET(actionFlags, A_STRING); /* expecting build cmd*/
|
||
}
|
||
|
||
void NEAR
|
||
assignBuildCommands()
|
||
{
|
||
BOOL okToFreeList = TRUE;
|
||
BOOL fFirstTarg = (BOOL)TRUE;
|
||
STRINGLIST *p;
|
||
char *which = NULL;
|
||
|
||
if (ON(actionFlags, A_RULE)) /* no macros found yet for*/
|
||
rules->buildCommands = list; /* inference rules */
|
||
else if (ON(actionFlags, A_SILENT)
|
||
|| ON(actionFlags, A_IGNORE)
|
||
|| ON(actionFlags, A_PRECIOUS)
|
||
|| ON(actionFlags, A_SUFFIX)) {
|
||
if (list) {
|
||
if (ON(actionFlags, A_SILENT))
|
||
which = silent;
|
||
else if (ON(actionFlags, A_IGNORE))
|
||
which = ignore;
|
||
else if (ON(actionFlags, A_PRECIOUS))
|
||
which = precious;
|
||
else if (ON(actionFlags, A_SUFFIX))
|
||
which = suffixes;
|
||
makeError(currentLine, CMDS_ON_PSEUDO, which);
|
||
}
|
||
}
|
||
else {
|
||
block->buildCommands = list;
|
||
block->buildMacros = macros;
|
||
block->flags = currentFlags;
|
||
while (p = targetList) { /* make a struct for each targ*/
|
||
if (doSpecial(p->text)) /* in list, freeing list when */
|
||
makeError(currentLine, MIXED_TARGETS);
|
||
makeTarget(p->text, fFirstTarg, &block); /* done, don't free name */
|
||
if (!makeTargets) { /* field -- it's still in use*/
|
||
makeTargets = p; /* if no targs given on cmdlin*/
|
||
okToFreeList = FALSE; /* put first target(s) from */
|
||
} /* mkfile in makeTargets list*/
|
||
targetList = p->next; /* (makeTargets defined in */
|
||
if (okToFreeList) /* nmake.c) */
|
||
FREE_STRINGLIST(p);
|
||
if (fFirstTarg)
|
||
fFirstTarg = (BOOL)FALSE;
|
||
}
|
||
}
|
||
targetList = NULL;
|
||
list = NULL;
|
||
macros = NULL;
|
||
block = NULL;
|
||
actionFlags = 0;
|
||
}
|
||
|
||
/* makeMacro -- define macro with name and string taken from global variables
|
||
*
|
||
* Modifies:
|
||
* fInheritUserEnv set to TRUE
|
||
*
|
||
* Notes:
|
||
* Calls putMacro() to place expanded Macros in the NMAKE table. By setting
|
||
* fInheritUserEnv those definitions that change Environment variables are
|
||
* inherited by the environment.
|
||
*/
|
||
void NEAR
|
||
makeMacro()
|
||
{
|
||
STRINGLIST *q;
|
||
char *t;
|
||
|
||
if (_ftcschr(name, '$')) { /* expand name */
|
||
q = macros;
|
||
t = expandMacros(name, ¯os); /* name holds result */
|
||
if (!*t) /* if macro to left of*/
|
||
makeError(currentLine, SYNTAX_NO_MACRO_NAME);/* = undefined, error*/
|
||
while (macros = q) {
|
||
q = q->next;
|
||
FREE_STRINGLIST(macros);
|
||
}
|
||
FREE(name);
|
||
name = t;
|
||
}
|
||
for (t = name; *t && MACRO_CHAR(*t); ++t); /* Check for illegal */
|
||
if (*t) makeError(currentLine, SYNTAX_BAD_CHAR, *t);/* chars. */
|
||
fInheritUserEnv = (BOOL)TRUE;
|
||
/* Put Env Var in Env & macros in table. */
|
||
if (!putMacro(name, string, 0)) {
|
||
FREE(name);
|
||
FREE(string);
|
||
}
|
||
name = string = NULL;
|
||
}
|
||
|
||
/* defineMacro -- check macro's syntax for illegal chars., then define it
|
||
*
|
||
* actions: check all of macro's characters
|
||
* if one's bad and it's an environment macro, bag it
|
||
* else flag error
|
||
* call putMacro to do the real work
|
||
*
|
||
* can't use macro invocation to left of = in macro def from commandline
|
||
* it doesn't make sense to do that, because we're not in a makefile
|
||
* the only way to get a comment char into the makefile w/o having it really
|
||
* mark a comment is to define a macro A=# on the command line
|
||
*/
|
||
|
||
BOOL NEAR
|
||
defineMacro(s, t, flags) /* commandline or env */
|
||
char *s; /* definitions */
|
||
char *t;
|
||
UCHAR flags;
|
||
{
|
||
char *u;
|
||
|
||
for (u = s; *u && MACRO_CHAR(*u); ++u); /* check for illegal */
|
||
if (*u) {
|
||
if (ON(flags, M_ENVIRONMENT_DEF)) { /* ignore bad macros */
|
||
return(FALSE);
|
||
}
|
||
makeError(currentLine, SYNTAX_BAD_CHAR, *u); /* chars, bad syntax */
|
||
}
|
||
return(putMacro(s, t, flags)); /* put macro in table */
|
||
}
|
||
|
||
/*** putMacro - Put the macro definition into the Macro Table / Environmnet ****
|
||
*
|
||
* Scope:
|
||
* Global.
|
||
*
|
||
* Purpose:
|
||
* putMacro() inserts a macro definition into NMAKE's macro table and also into
|
||
* the environment. If a macro name is also an environment variable than its
|
||
* value is inherited into the environment. While replacing older values by new
|
||
* values NMAKE needs to follow the precedence of macro definitions which is
|
||
* as per the notes below.
|
||
*
|
||
* Input:
|
||
* name - Name of the macro
|
||
* value - Value of the macro
|
||
* flags - Flags determining Precedence of Macro definitions (see Notes)
|
||
*
|
||
* Output:
|
||
*
|
||
* Errors/Warnings:
|
||
* OUT_OF_ENV_SPACE - If putenv() returns failure in adding to the environment.
|
||
*
|
||
* Assumes:
|
||
* Whatever it assumes
|
||
*
|
||
* Modifies Globals:
|
||
* fInheritUserEnv - Set to False.
|
||
*
|
||
* Uses Globals:
|
||
* fInheritUserEnv - If True then Inherit definition to the Environment.
|
||
* gFlags - Global Options Flag. If -e specified then Environment Vars
|
||
* take precedence.
|
||
* macroTable - NMAKE's internal table of Macro Definitions.
|
||
*
|
||
* Notes:
|
||
* 1> If the same macro is defined in more than one place then NMAKE uses the
|
||
* following order of Precedence (highest to lowest) --
|
||
*
|
||
* -1- Command line definitions
|
||
* -2- Description file/Include file definitions
|
||
* -3- Environment definitions
|
||
* -4- TOOLS.INI definitions
|
||
* -5- Predefined Values (e.g. for CC, AS, BC, RC)
|
||
* If -e option is specified then -3- precedes -2-.
|
||
*
|
||
* 2> Check if the macro already exists in the Macro Table. If the macro is not
|
||
* redefinable (use order of precedence) then return. Make a new string
|
||
* element to hold macro's new value. If the macro does not exist then create
|
||
* new entry in the Macro table. Set Macro's flag to be union of Old and new
|
||
* values. Add the new value to macro's value entry. If a new macro then add
|
||
* it to the macro table. Test for Cyclic definitions.
|
||
*
|
||
* Undone/Incomplete:
|
||
* 1> Investigate into possibility of removing fInheritUserEnv variable.
|
||
* Can be done. Use CANT_REDEFINE(p) || OFF((A)->flags,M_ENVIRONMENT_DEF)
|
||
* 2> Probably should warn when $(MAKE) is being changed.
|
||
*
|
||
*******************************************************************************/
|
||
BOOL NEAR
|
||
putMacro(name, value, flags)
|
||
char *name;
|
||
char *value;
|
||
UCHAR flags;
|
||
{
|
||
MACRODEF *p;
|
||
STRINGLIST *q;
|
||
BOOL defined = FALSE;
|
||
BOOL fSyntax = TRUE;
|
||
|
||
/*
|
||
* Inherit macro definitions. Call removeMacros() to expand sub-macro
|
||
* definitions. Must be done before macro is put in table, else
|
||
* recursive definitions won't work.
|
||
*/
|
||
if (ON(flags, M_NON_RESETTABLE)) {
|
||
if (*value)
|
||
if ((putEnvStr(name,removeMacros(value)) == -1))
|
||
makeError(currentLine, OUT_OF_ENV_SPACE);
|
||
}
|
||
else
|
||
if (fInheritUserEnv && OFF(gFlags, F1_USE_ENVIRON_VARS) && getenv(name)) {
|
||
if (p = findMacro(name)) { /* don't let user */
|
||
if (CANT_REDEFINE(p)) /* redefine cmdline */
|
||
return(FALSE); /* macros, MAKE, etc.*/
|
||
}
|
||
if ((putEnvStr(name,removeMacros(value)) == -1))
|
||
makeError(currentLine, OUT_OF_ENV_SPACE);
|
||
}
|
||
fInheritUserEnv = (BOOL)FALSE;
|
||
if (p = findMacro(name)) { /* don't let user */
|
||
if (CANT_REDEFINE(p)) /* redefine cmdline */
|
||
return(FALSE); /* macros, MAKE, etc.*/
|
||
}
|
||
q = makeNewStrListElement();
|
||
q->text = value;
|
||
if (!p) {
|
||
p = makeNewMacro();
|
||
p->name = name;
|
||
assert(p->flags == 0);
|
||
assert(p->values == NULL);
|
||
}
|
||
else defined = TRUE;
|
||
p->flags &= ~M_UNDEFINED; /* Is no longer undefined */
|
||
p->flags |= flags; /* Set flags to union of old and new */
|
||
prependItem((STRINGLIST**)&(p->values), (STRINGLIST*)q);
|
||
if (!defined)
|
||
insertMacro((STRINGLIST*)p);
|
||
if (_ftcschr(value, '$')) {
|
||
/* Check for cyclic Macro Definitions */
|
||
SET(p->flags, M_EXPANDING_THIS_ONE);
|
||
/* NULL -> don't build list */
|
||
fSyntax = findMacroValues(value, NULL, NULL, name, 1, 0, flags);
|
||
CLEAR(p->flags, M_EXPANDING_THIS_ONE);
|
||
}
|
||
if (!fSyntax) {
|
||
p->values = NULL;
|
||
p->flags &= M_UNDEFINED;
|
||
return(FALSE);
|
||
}
|
||
return(TRUE);
|
||
}
|
||
|
||
/*** makeRule -- makes an inference rule ***************************************
|
||
*
|
||
* Scope:
|
||
* Local
|
||
*
|
||
* Purpose:
|
||
* Allocates space for an inference rule and adds rule to the beginning of the
|
||
* doubly linked inference rule list. The name of the rule is also added.
|
||
*
|
||
* Input:
|
||
* rule -- The name of the inference rule
|
||
*
|
||
* Output:
|
||
*
|
||
* Errors/Warnings:
|
||
*
|
||
* Assumes:
|
||
*
|
||
* Modifies Globals:
|
||
* rules -- The doubly linked inference rule list to which the rule is added
|
||
*
|
||
* Uses Globals:
|
||
*
|
||
* Notes:
|
||
* The syntax of an inference rule is --
|
||
*
|
||
* {frompath}.fromext{topath}.toext: # Name of the inference rule
|
||
* command ... # command block of the inference rule
|
||
*
|
||
*******************************************************************************/
|
||
LOCAL void NEAR
|
||
makeRule(rule)
|
||
STRINGLIST *rule;
|
||
{
|
||
RULELIST *rList;
|
||
|
||
rList = makeNewRule();
|
||
rList->name = rule->text;
|
||
prependItem((STRINGLIST**)&rules, (STRINGLIST*)rList);
|
||
if (rList->next)
|
||
rList->next->back = rList;
|
||
}
|
||
|
||
/*
|
||
* makeTarget -- add target to targetTable
|
||
*
|
||
* actions: if no block defined, create one and initialize it
|
||
* make new build list entry for this target
|
||
* if the target's already in the table,
|
||
* flag error if : and :: mixed
|
||
* else add new buildlist object to target's current buildlist
|
||
* else allocate new object, initialize it, and stick it in table
|
||
*/
|
||
/*** makeTarget -- make a target entry in the targetTable **********************
|
||
*
|
||
* Scope:
|
||
* Local.
|
||
*
|
||
* Purpose:
|
||
* what it does and how
|
||
*
|
||
* Input:
|
||
* param -- what for, etc
|
||
*
|
||
* Output:
|
||
* Returns ...
|
||
*
|
||
* Errors/Warnings:
|
||
* error/warning -- what and why
|
||
*
|
||
* Assumes:
|
||
* Whatever it assumes
|
||
*
|
||
* Modifies Globals:
|
||
* global -- how/what
|
||
*
|
||
* Uses Globals:
|
||
* global used and why
|
||
*
|
||
* Notes:
|
||
* explanations/comments etc
|
||
*
|
||
*******************************************************************************/
|
||
LOCAL void NEAR
|
||
makeTarget(s, firstTarg, block)
|
||
char *s;
|
||
BOOL firstTarg;
|
||
BUILDBLOCK **block;
|
||
{
|
||
BUILDLIST *build;
|
||
MAKEOBJECT *object;
|
||
|
||
if (!*block)
|
||
*block = makeNewBuildBlock();
|
||
if (firstTarg) {
|
||
build = makeNewBldListElement();
|
||
build->buildBlock = *block;
|
||
}
|
||
else build = makeBuildList(*block);
|
||
if (object = findTarget(s)) {
|
||
if (ON(object->flags2, F2_DOUBLECOLON)
|
||
!= ON(currentFlags, F2_DOUBLECOLON))
|
||
makeError(currentLine, MIXED_SEPARATORS);
|
||
appendItem((STRINGLIST**)&(object->buildList), (STRINGLIST*)build);
|
||
FREE(s);
|
||
}
|
||
else {
|
||
build->next = NULL;
|
||
object = makeNewObject();
|
||
object->name = s;
|
||
object->buildList = build;
|
||
object->flags2 = currentFlags;
|
||
prependItem((STRINGLIST**)targetTable
|
||
+hash(s, MAXTARGET, (BOOL)TRUE),
|
||
(STRINGLIST*)object);
|
||
}
|
||
}
|
||
|
||
LOCAL void NEAR
|
||
clearSuffixes()
|
||
{
|
||
STRINGLIST *p;
|
||
|
||
while (p = dotSuffixList) {
|
||
dotSuffixList = dotSuffixList->next;
|
||
FREE(p->text);
|
||
FREE_STRINGLIST(p);
|
||
}
|
||
}
|
||
|
||
LOCAL void NEAR
|
||
appendPseudoTargetList(pseudo, list)
|
||
STRINGLIST **pseudo;
|
||
STRINGLIST *list;
|
||
{
|
||
STRINGLIST *p,
|
||
*q,
|
||
*r;
|
||
char *t,
|
||
*u;
|
||
|
||
while (p = list) {
|
||
if (!_ftcschr(p->text, '$')) {
|
||
list = list->next;
|
||
p->next = NULL;
|
||
appendItem(pseudo, p);
|
||
}
|
||
else {
|
||
r = macros;
|
||
t = expandMacros(p->text, ¯os);
|
||
while (r != macros) {
|
||
q = r->next;
|
||
FREE_STRINGLIST(r);
|
||
r = q;
|
||
}
|
||
for (u = _ftcstok(t, " \t"); u; u = _ftcstok(NULL, " \t")) {
|
||
q = makeNewStrListElement();
|
||
q->text = makeString(u);
|
||
appendItem(pseudo, q);
|
||
}
|
||
FREE(t);
|
||
FREE(p->text);
|
||
list = list->next;
|
||
FREE_STRINGLIST(p);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*** putEnvStr -- Extends putenv() standard function
|
||
*
|
||
* Purpose:
|
||
* Library function putenv() expects one string argument of the form
|
||
* "NAME=value"
|
||
* Most of the times when putenv() is to be used we have two strings
|
||
* name -- of the variable to add to the environment, and
|
||
* value -- to be set
|
||
* putEnvStr takes these 2 parameters and calls putenv with the reqd
|
||
* format
|
||
*
|
||
* Input:
|
||
* name -- of var to add to the env
|
||
* value -- reqd to be set
|
||
*
|
||
* Output:
|
||
* Same as putenv()
|
||
*
|
||
*******************************************************/
|
||
|
||
int NEAR
|
||
putEnvStr(name, value)
|
||
char *name;
|
||
char *value;
|
||
{
|
||
char *envPtr;
|
||
envPtr = (char *)rallocate(_ftcslen(name)+1+_ftcslen(value)+1);
|
||
/* ^ ^
|
||
* for '=' for '\0'
|
||
*/
|
||
|
||
return(PutEnv(_ftcscat(_ftcscat(_ftcscpy(envPtr, name), "="), value)));
|
||
}
|
||
|
||
/*** makeBuildList -- takes a build block and copies into a buildlist ******
|
||
*
|
||
* Purpose:
|
||
* Routine creates a copy of a buildlist and returns a pointer to a copy.
|
||
* When multiple targets have the same description block then there is a
|
||
* need for each of them to get seperate build blocks. makeBuildList()
|
||
* helps achieve this by creating a copy for each target.
|
||
*
|
||
* Input:
|
||
* bBlock -- the build block whose copy is to be added to a build block
|
||
*
|
||
* Output:
|
||
* Returns a pointer to the copy of buildlist it creates
|
||
*
|
||
***************************************************************************/
|
||
BUILDLIST * NEAR
|
||
makeBuildList(bBlock)
|
||
BUILDBLOCK *bBlock;
|
||
{
|
||
BUILDLIST *tList = makeNewBldListElement();
|
||
BUILDBLOCK *tBlock = makeNewBuildBlock();
|
||
|
||
tBlock->dependents = bBlock->dependents;
|
||
tBlock->dependentMacros = bBlock->dependentMacros;
|
||
tBlock->buildCommands = bBlock->buildCommands;
|
||
tBlock->buildMacros = bBlock->buildMacros;
|
||
tBlock->flags = bBlock->flags;
|
||
tBlock->dateTime = bBlock->dateTime;
|
||
|
||
tList->buildBlock = tBlock;
|
||
return(tList);
|
||
}
|
||
|
||
/*** makeList -- creates a copy of a stringlist and returns a copy *********
|
||
*
|
||
* Purpose:
|
||
* Routine creates a copy of a stringlist and returns a pointer to a copy.
|
||
*
|
||
* Input:
|
||
* list -- the list whose copy is to be made
|
||
*
|
||
* Output:
|
||
* Returns a pointer to the copy of the list it creates
|
||
*
|
||
***************************************************************************/
|
||
STRINGLIST * NEAR
|
||
makeList(list)
|
||
STRINGLIST *list;
|
||
{
|
||
STRINGLIST *t = list,
|
||
*pList,
|
||
*qList = NULL,
|
||
*tList = NULL;
|
||
for ( ; t; t = t->next) {
|
||
pList = makeNewStrListElement();
|
||
pList->text = makeString(t->text);
|
||
|
||
if (qList == NULL) {
|
||
qList = tList = pList;
|
||
}
|
||
else {
|
||
qList->next = pList;
|
||
qList = pList;
|
||
}
|
||
}
|
||
return(tList);
|
||
}
|