mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
349 lines
11 KiB
349 lines
11 KiB
/*** COMMAND.C - NMAKE 'command line' handling routines ************************
|
|
*
|
|
* Copyright (c) 1988-1990, Microsoft Corporation. All rights reserved.
|
|
*
|
|
* Purpose:
|
|
* Module contains routines to handle NMAKE 'command line' syntax. NMAKE can be
|
|
* optionally called by using the syntax 'NMAKE @commandfile'. This allows more
|
|
* flexibility and preents a way of getting around DOS's 128-byte limit on the
|
|
* length of a command line. Additionally, it saves keystrokes for frequently
|
|
* run commands for NMAKE.
|
|
*
|
|
* Revision History:
|
|
* 15-Nov-1993 JR Major speed improvements
|
|
* 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*
|
|
* 14-Aug-1992 SS CAVIAR 2735: handle quoted macro values in command files
|
|
* 02-Feb-1990 SB Replace fopen() by FILEOPEN
|
|
* 01-Dec-1989 SB Changed realloc() to REALLOC()
|
|
* 22-Nov-1989 SB Changed free() to FREE()
|
|
* 17-Aug-1989 SB Add error check to closing file
|
|
* 05-Apr-1989 SB made func calls NEAR to put all funcs into 1 module
|
|
* 20-Oct-1988 SB Notes added to readCommandFile()
|
|
* 17-Aug-1988 RB Clean up.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include "nmake.h"
|
|
#include "nmmsg.h"
|
|
#include "proto.h"
|
|
#include "globals.h"
|
|
#include "grammar.h"
|
|
|
|
LOCAL void NEAR addArgument(char*,unsigned,char***);
|
|
LOCAL void NEAR processLine(char*,unsigned*,char***);
|
|
LOCAL void NEAR tokenizeLine(char*,unsigned*,char***);
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* readCommandFile()
|
|
*
|
|
* arguments: name pointer to name of command file to read
|
|
*
|
|
* actions: opens command file
|
|
* reads in lines and calls processLine() to
|
|
* break them into tokens and to build
|
|
* an argument vector (a la argv[])
|
|
* calls parseCommandLine() recursively to process
|
|
* the accumulated "command line" arguments
|
|
* frees space used by the arg vector
|
|
*
|
|
* modifies: makeFiles in main() by modifying contents of parameter
|
|
* list
|
|
* makeTargets in main() by modifying contents of targets
|
|
* parameter
|
|
* buf global buffer
|
|
*
|
|
* notes: function is not ANSI portable because it uses fopen()
|
|
* with "rt" type and text mode is a Microsoft extension
|
|
*
|
|
*/
|
|
|
|
|
|
void NEAR
|
|
readCommandFile(name)
|
|
char *name;
|
|
{
|
|
char *s, /* buffer */
|
|
**vector; /* local versions of */
|
|
unsigned count = 0, /* arg vector, count */
|
|
n;
|
|
|
|
if (!(file = FILEOPEN(name,"rt")))
|
|
makeError(0,CANT_OPEN_FILE,name);
|
|
vector = NULL; /* no args yet */
|
|
while (fgets(buf,MAXBUF,file)) {
|
|
n = _ftcslen(buf);
|
|
/* if we didn't get the whole line, OR
|
|
* the line ended with a backSlash
|
|
*/
|
|
if ((n == MAXBUF-1 && buf[n-1] != '\n')
|
|
|| (buf[n-1] == '\n' && buf[n-2] == '\\')) {
|
|
if (buf[n-2] == '\\') {
|
|
//Replace \n by \0 and \\ by a space; Also reset length
|
|
buf[n-1] = '\0';
|
|
buf[n-2] = ' ';
|
|
n--;
|
|
}
|
|
s = makeString(buf);
|
|
getRestOfLine(&s,&n);
|
|
}
|
|
else s = buf;
|
|
processLine(s,&count,&vector); /* separate into args */
|
|
if (s != buf) FREE(s);
|
|
}
|
|
if (fclose(file) == EOF)
|
|
makeError(0, ERROR_CLOSING_FILE, name);
|
|
parseCommandLine(count,vector); /* evaluate the args */
|
|
while (count--) /* free the arg vector*/
|
|
if(vector[count]) FREE(vector[count]); /* NULL entries mean */
|
|
FREE(vector); /* that the space the*/
|
|
} /* entry used to pt */
|
|
/* to is still in use*/
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* getRestOfLine()
|
|
*
|
|
* arguments: s pointer to readCommandFile()'s buffer
|
|
* holding line so far
|
|
* n pointer to readCommandFile()'s count of
|
|
* the chars in *s
|
|
*
|
|
* actions: keeps reading in text until it sees a newline
|
|
* or the end of file
|
|
* reallocs space for the old buffer plus the
|
|
* contents of the new buffer each time
|
|
* appends new buffer's text to existing text
|
|
*
|
|
* modifies: s readCommandFile()'s text buffer by realloc'ing
|
|
* more space for incoming text
|
|
* n readCommandFile()'s count of bytes in s
|
|
* buf global buffer
|
|
*/
|
|
|
|
void NEAR
|
|
getRestOfLine(s,n)
|
|
char *s[];
|
|
unsigned *n;
|
|
{
|
|
unsigned temp;
|
|
char *t;
|
|
|
|
t = buf;
|
|
while ((*s)[*n-1] != '\n') { /* get rest of line */
|
|
if (!fgets(t,MAXBUF,file))
|
|
break; /* we hit EOF */
|
|
temp = _ftcslen(t);
|
|
if (t[temp-2] == '\\' && t[temp-1] == '\n') {
|
|
//Replace \n by \0 and \\ by a space; Also reset length
|
|
t[temp-1] = '\0';
|
|
t[temp-2] = ' ';
|
|
}
|
|
temp = *n;
|
|
*n += _ftcslen(t);
|
|
*s = REALLOC(*s,*n+1); /* + 1 for NULL byte */
|
|
if (!*s)
|
|
makeError(line, MACRO_TOO_LONG);
|
|
_ftcscpy(*s+temp,t);
|
|
}
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* processLine()
|
|
*
|
|
* arguments: s pointer to readCommandFile()'s buffer
|
|
* holding "command line" to be processed
|
|
* count pointer to readCommandFile()'s count of
|
|
* "command line" arguments seen so far
|
|
* vector pointer to readCommandFile()'s vector of
|
|
* pointers to character strings
|
|
*
|
|
* actions: if the line to be broken into "command line arguments"
|
|
* contains '"'
|
|
* breaks all the text before '"' into tokens
|
|
* delimited by whitespace (which get put in
|
|
* vector[] by tokenizeLine())
|
|
* finds the closing '"' and treats the quoted string
|
|
* as a single token, adding it to the vector
|
|
* recurses on the tail of the line (to check for
|
|
* other quoted strings)
|
|
* else breaks all text in line into tokens delimited
|
|
* by whitespace
|
|
*
|
|
* modifies: vector readCommandFile()'s vector of pointers to
|
|
* "command line argument" strings (by modifying
|
|
* the contents of the parameter pointer, vector)
|
|
* count readCommandFile()'s count of the arguments in
|
|
* the vector (by modifying the contents of the
|
|
* parameter pointer, count)
|
|
*/
|
|
|
|
|
|
LOCAL void NEAR
|
|
processLine(s,count,vector)
|
|
char *s;
|
|
unsigned *count;
|
|
char **vector[];
|
|
{
|
|
char *t;
|
|
char *u;
|
|
unsigned m;
|
|
unsigned n;
|
|
BOOL allocFlag = FALSE;
|
|
|
|
if (!(t = _ftcschr(s,'"'))) /* no quoted strings, */
|
|
tokenizeLine(s,count,vector); /* just standard fare*/
|
|
else {
|
|
/* There are two kinds of situations in which quotes can occur:
|
|
1. "FOO = bar baz"
|
|
2. FOO="bar baz"
|
|
*/
|
|
|
|
if ((t == s) || (*(t-1) != '=')) // Case 1 above
|
|
{ /* if line contains */
|
|
*t++ = '\0'; /* quoted macrodef */
|
|
tokenizeLine(s,count,vector); /* get tokens before "*/
|
|
}
|
|
else // Case 2 above
|
|
{
|
|
*t-- = ' ';
|
|
for (u = t; u > s; --u) // find the beginning of the macro name
|
|
if (*u == ' ' || *u == '\t' || *u == '\n')
|
|
break;
|
|
|
|
if (u != s)
|
|
{
|
|
*u++ = '\0';
|
|
tokenizeLine(s, count, vector);
|
|
}
|
|
|
|
t = u;
|
|
}
|
|
|
|
n = _ftcslen(t);
|
|
for (u = t; *u; ++u) { /* look for closing " */
|
|
if (*u == '"') { /* need " and not "" */
|
|
if (*(u+1) == '"') {
|
|
_ftcscpy(u,u+1);
|
|
continue;
|
|
}
|
|
*u++ = '\0'; /* terminate macrodef */
|
|
addArgument(t,*count,vector); /* treat as one arg */
|
|
++*count;
|
|
processLine(u+1,count,vector); /* recurse on rest of */
|
|
break; /* line */
|
|
} /* TAIL RECURSION -- */
|
|
if ((*u == '\\') /* eliminate later? */
|
|
&& WHITESPACE(*(u-1))
|
|
&& (*(u+1) == '\n')) { /* \n always last char*/
|
|
/***
|
|
*u++ = ' '; * \\n becomes a space*
|
|
***/
|
|
*u = '\0'; /* 2 chars go to 1 */
|
|
m = (n = n-2); /* adjust length count*/
|
|
if (!allocFlag) {
|
|
allocFlag = TRUE;
|
|
t = makeString(t);
|
|
}
|
|
getRestOfLine(&t,&n); /* get some more text */
|
|
/**
|
|
u = t + m - 1; * reset u & continue *
|
|
**/
|
|
u = t + m ; /* reset u & continue */
|
|
} /* looping */
|
|
}
|
|
if (u == t + n) /* if at end of line */
|
|
makeError(0,SYNTAX_NO_QUOTE); /* and no ", error */
|
|
if (allocFlag) FREE(t);
|
|
}
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* tokenizeLine()
|
|
*
|
|
* arguments: s pointer to readCommandFile()'s buffer
|
|
* holding "command line" to be tokenized
|
|
* count pointer to readCommandFile()'s count of
|
|
* "command line" arguments seen so far
|
|
* vector pointer to readCommandFile()'s vector of
|
|
* pointers to character strings
|
|
*
|
|
* actions: breaks the line in s into tokens (command line
|
|
* arguments) delimited by whitespace
|
|
* adds each token to the argument vector
|
|
* adjusts the argument counter
|
|
*
|
|
* modifies: vector readCommandFile()'s vector of pointers to
|
|
* "command line argument" strings (by modifying
|
|
* the contents of the parameter pointer, vector)
|
|
* count readCommandFile()'s count of the arguments in
|
|
* the vector (by modifying the contents of the
|
|
* parameter pointer, count)
|
|
*
|
|
* If the user ever wants '@' to be part of an argument in a command file,
|
|
* he has to enclose that argument in quotation marks.
|
|
*/
|
|
|
|
LOCAL void NEAR
|
|
tokenizeLine(s,count,vector) /* gets args delimited*/
|
|
char *s; /* by whitespace and */
|
|
unsigned *count; /* constructs an arg */
|
|
char **vector[]; /* vector */
|
|
{
|
|
char *t;
|
|
|
|
if (t = _ftcschr(s,'\\'))
|
|
if (WHITESPACE(*(t-1)) && (*(t+1) == '\n'))
|
|
*t = '\0';
|
|
|
|
for (t = _ftcstok(s," \t\n"); t; t = _ftcstok(NULL," \t\n")) {
|
|
if (*t == '@') {
|
|
makeError(0,SYNTAX_CMDFILE,t+1);
|
|
break; /* should we keep on */
|
|
} /* parsing here? */
|
|
addArgument(t,*count,vector);
|
|
++*count;
|
|
}
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------------
|
|
* addArgument()
|
|
*
|
|
* arguments: s pointer to text of argument to be added
|
|
* to the "command line argument" vector
|
|
* count pointer to readCommandFile()'s count of
|
|
* "command line" arguments seen so far
|
|
* vector pointer to readCommandFile()'s vector of
|
|
* pointers to character strings
|
|
*
|
|
* actions: allocates space in the vector for the new argument
|
|
* allocates space for argument string
|
|
* makes vector entry point to argument string
|
|
*
|
|
* modifies: vector readCommandFile()'s vector of pointers to
|
|
* "command line argument" strings (by modifying
|
|
* the contents of the parameter pointer, vector)
|
|
* (count gets incremented by caller)
|
|
*
|
|
* To keep from fragmenting memory by doing many realloc() calls for very
|
|
* small amounts of space, we get memory in small chunks and use that until
|
|
* it is depleted, then we get another chunk . . . .
|
|
*/
|
|
|
|
LOCAL void NEAR
|
|
addArgument(s,count,vector) /* puts s in vector */
|
|
char *s;
|
|
unsigned count;
|
|
char **vector[];
|
|
{
|
|
if (!(*vector))
|
|
*vector = (char**) allocate(CHUNKSIZE*sizeof(char*));
|
|
else if (!(count % CHUNKSIZE)) {
|
|
*vector = (char**) REALLOC(*vector,(count+CHUNKSIZE)*sizeof(char*));
|
|
if (!*vector) makeError(0,OUT_OF_MEMORY);
|
|
}
|
|
(*vector)[count] = makeString(s);
|
|
}
|