// 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: // 04-Feb-2000 BTF Ported to Win64 // 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 "precomp.h" #pragma hdrstop void addArgument(char*,unsigned,char***); void processLine(char*,unsigned*,char***); void 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 readCommandFile( char *name ) { char *s, // buffer **vector; // local versions of arg vector unsigned count = 0; // count size_t n; if (!(file = FILEOPEN(name,"rt"))) makeError(0,CANT_OPEN_FILE,name); vector = NULL; // no args yet while (fgets(buf,MAXBUF,file)) { n = _tcslen(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] == '\\' && buf[n-1] == '\n') { // 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 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 getRestOfLine( char *s[], size_t *n ) { size_t temp; char *t; t = buf; while ((*s)[*n-1] != '\n') { // get rest of line if (!fgets(t,MAXBUF,file)) break; // we hit EOF temp = _tcslen(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 += _tcslen(t); { void *pv = REALLOC(*s,*n+1); // + 1 for NULL byte if (!pv) makeError(line, MACRO_TOO_LONG); else *s = (char *)pv; } _tcscpy(*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) void processLine( char *s, unsigned *count, char **vector[] ) { char *t; char *u; size_t m; size_t n; BOOL allocFlag = FALSE; if (!(t = _tcschr(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 *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 = _tcslen(t); for (u = t; *u; ++u) { // look for closing " if (*u == '"') { // need " and not "" if (*(u+1) == '"') { _tcscpy(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 line break; } // TAIL RECURSION -- eliminate later? if ((*u == '\\') && WHITESPACE(*(u-1)) && (*(u+1) == '\n')) { // \n always last char *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 ; // 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. void tokenizeLine( // gets args delimited char *s, // by whitespace and unsigned *count, // constructs an arg char **vector[] // vector ) { char *t; if (t = _tcschr(s,'\\')) { if (WHITESPACE(*(t-1)) && (*(t+1) == '\n')) { *t = '\0'; } } for (t = _tcstok(s," \t\n"); t; t = _tcstok(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 . . . . void addArgument( // puts s in vector char *s, unsigned count, char **vector[] ) { if (!(*vector)) { *vector = (char**) allocate(CHUNKSIZE*sizeof(char*)); } else if (!(count % CHUNKSIZE)) { void *pv = REALLOC(*vector,(count+CHUNKSIZE)*sizeof(char*)); if (!pv) { makeError(0,OUT_OF_MEMORY); } else { *vector = (char**) pv; } } (*vector)[count] = makeString(s); }