#include #include #include #include #include extern BOOL fVerbose; // linked in from binplace.c // structure for recording current state of // an argument vector typedef struct _ARG_INFO { TCHAR** ArgV; INT MaxArgC; INT NextArgC; } ARG_INFO; // global variables for same static ARG_INFO RespFileArgs; static ARG_INFO CmdLineArgs; // flag to let us know if we're currently in a response // file or not. Initially, we're not so init to FALSE static BOOL fInRespFile = FALSE; // flag to let us know if this is the first call to GetNextArg() static BOOL fFirstCall = TRUE; // CRT global used by _setargv() _CRTIMP extern char *_acmdln; // struct for __getmainargs() typedef struct { int newmode; } _startupinfo; // decl for _getmainargs() - expands _acmdln to (__argc, __argv) _CRTIMP int __cdecl __getmainargs (int *pargc, char ***pargv, char ***penvp, int dowildcard, _startupinfo * startinfo); // local functions DWORD CopyArgIntoBuffer(TCHAR* Dst, TCHAR* Src, INT DstSize); /* ------------------------------------------------------------------------------------------------ GetNextArgSize : returns the size required to hold the next argument ** SEE ASSUMPTIONS AND LIMITATIONS REGARDING GetNextArg() BELOW ** ------------------------------------------------------------------------------------------------ */ DWORD GetNextArgSize(void) { TCHAR TempBuffer[1]; // minimal buffer to pass to GetNextArg DWORD dwNextRequiredSize = 0; // return value DWORD dwTempValue; // temp DWORD for GetNextArg() return value // instead of mimicing GetNextArg, we'll just call it then rollback the correct globals dwTempValue = GetNextArg(TempBuffer, 1, &dwNextRequiredSize); // roll back the relevent global if (fInRespFile) { RespFileArgs.NextArgC--; } else { CmdLineArgs.NextArgC--; } return(dwNextRequiredSize); } /* ------------------------------------------------------------------------------------------------ GetNextArg : returns next argument in array including opening and expending reponse files given on the command line Buffer is the buffer to copy the next argument into BufferSize is the size of Buffer in characters RequiredSize, if not NULL, is set to the actual size required for the parameter (inluding '\0'). This is useful for determining whether a return value of 0 is an error or end of arguments. Return value is the number of characters copied including \0. if return value is 0 and RequiredSize is non-zero, an error occurred - check GetLastError() if return value is 0 and RequiredSize is zero, no more arguments are available Assumptions: the calling program does not modify _acmdln, __argv, __argc the calling program ignores main's (int, char**) Limitations: does not work with UNICODE response files (not tested when built w/ -D_UNICODE) response files may not include response files literal parameter beginning with '@' is returned if: - parameter is found in a response file --OR-- - the named file cannot be opened --OR-- - the named file won't fit in memory environment strings in repsonse file are not expanded ------------------------------------------------------------------------------------------------ */ DWORD GetNextArg(OUT TCHAR* Buffer, IN DWORD BufferSize, OPTIONAL OUT DWORD* RequiredSize) { DWORD dwReturnValue; // value to return HANDLE* RespFile; // for reading in a new response file TCHAR* TempBuffer; TCHAR* pTchar; TCHAR* pBeginBuffer; DWORD dwSize; // first-call initialization if (fFirstCall) { CmdLineArgs.ArgV = __argv; // set to initial __argv CmdLineArgs.MaxArgC = __argc; // set to initial __argc CmdLineArgs.NextArgC = 0; // no args used yet RespFileArgs.ArgV = NULL; RespFileArgs.MaxArgC = 0; RespFileArgs.NextArgC = -1; fFirstCall = FALSE; } // buffer cannot be null if (Buffer == NULL) { SetLastError(ERROR_INVALID_PARAMETER); dwReturnValue = 0; if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0113: Passed NULL buffer\n"); } else { // handle getting next arg when reading a response file if (fInRespFile) { // was the previous arg the last one from the file if (RespFileArgs.NextArgC >= RespFileArgs.MaxArgC) { if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0127: Response file finished\n"); // yes, so clear the flag and skip to reading the next value from the cmdline fInRespFile = FALSE; goto UseArgV; } else { // fill in the required size if (RequiredSize != NULL) { *RequiredSize = _tcsclen(RespFileArgs.ArgV[RespFileArgs.NextArgC])+1; } // no, so fill in Buffer and advanced NextArgC dwReturnValue = CopyArgIntoBuffer(Buffer, RespFileArgs.ArgV[RespFileArgs.NextArgC++], BufferSize); } // handle getting next cmdline arg } else { UseArgV: // was the previous arg the last one? if (CmdLineArgs.NextArgC >= CmdLineArgs.MaxArgC) { // yes, so set safe return values if (RequiredSize != NULL) { *RequiredSize = 0; } Buffer[0] = TEXT('\0'); dwReturnValue = 0; if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0127: Command line finished\n"); } else { // no, so ge the next arg // is the next arg a response file? if (CmdLineArgs.ArgV[CmdLineArgs.NextArgC][0] == '@') { // yes, try to open it RespFile=CreateFile((CmdLineArgs.ArgV[CmdLineArgs.NextArgC]+1), // don't include '@' GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,(HANDLE)NULL); if (RespFile != INVALID_HANDLE_VALUE) { if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0174: Using response file: %s \n",(CmdLineArgs.ArgV[CmdLineArgs.NextArgC]+1)); // advance CmdLineArgs.NextArgC so we don't try to load the file again as // soon as it's finished CmdLineArgs.NextArgC++; // file opened - get the size required to load it into memory dwSize=GetFileSize(RespFile,NULL); // try to get enough memory to load the file TempBuffer = (TCHAR*)malloc(sizeof(TCHAR)*(dwSize+1)); if (TempBuffer != NULL) { // store a pointer to the start of the buffer pBeginBuffer = TempBuffer; // zero the memory then load the file ZeroMemory(TempBuffer, _msize(TempBuffer)); ReadFile(RespFile,TempBuffer, _msize(TempBuffer),&dwSize,NULL); // ensure NULL termination TempBuffer[dwSize]='\0'; // map \r and \n to spaces because _setargv won't do it for us while (pTchar=strchr(TempBuffer,'\r')) { *pTchar = ' '; pTchar++; if (*pTchar == '\n') { *pTchar = ' '; } } // setargv dislikes odd leading characters, so step past them while ( ( (!isprint(TempBuffer[0])) || ( isspace(TempBuffer[0])) ) && ( TempBuffer[0] != 0 ) ) { TempBuffer++; } // how to handle this case? response file existed but contained no data?? if (_tcsclen(TempBuffer) > 0) { // required for call to getmainargs() _startupinfo SInfo = {0}; CHAR** Unused; // _setargv() expects _acmdln to point to the string to convert _acmdln = TempBuffer; // actually convert the string if ( __getmainargs(&RespFileArgs.MaxArgC, &RespFileArgs.ArgV, &Unused, 1, &SInfo) < 0 ) { if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0230: Failed to get args from response file- skipping it\n"); goto UseArgV; } // clean up temp resources free(pBeginBuffer); CloseHandle(RespFile); // init the global structure RespFileArgs.NextArgC = 0; fInRespFile = TRUE; // fill in the required size if (RequiredSize != NULL) { *RequiredSize = _tcsclen(RespFileArgs.ArgV[RespFileArgs.NextArgC])+1; } // fill in Buffer dwReturnValue = CopyArgIntoBuffer(Buffer, RespFileArgs.ArgV[RespFileArgs.NextArgC++], BufferSize); } else { // file contains no parameters if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0253: Empty response file- ignoring\n"); // clean up temp resources free(pBeginBuffer); CloseHandle(RespFile); // Instead of returning the literal filename, skip over the file and just return the next arg goto UseArgV; // // fill in the required size // if (RequiredSize != NULL) { // *RequiredSize = _tcsclen(CmdLineArgs.ArgV[CmdLineArgs.NextArgC-1])+1; // } // // // fill in Buffer and advanced NextArgC // dwReturnValue = CopyArgIntoBuffer(Buffer, CmdLineArgs.ArgV[CmdLineArgs.NextArgC-1], BufferSize); } } else { // not enough memory to load the file- not sure what the best way to handle this is if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0272: Out of memory\n"); SetLastError(ERROR_NOT_ENOUGH_MEMORY); if (RequiredSize != NULL) { *RequiredSize = 1; // set to non-zero value } Buffer[0] = TEXT('\0'); dwReturnValue = 0; } } else { // it *looked* like a response file, but couldn't be opened, so return it literally if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0277: Can't open response file %s\n",(CmdLineArgs.ArgV[CmdLineArgs.NextArgC]+1)); // fill in the required size if (RequiredSize != NULL) { *RequiredSize = _tcsclen(CmdLineArgs.ArgV[CmdLineArgs.NextArgC])+1; } // fill in Buffer and advanced NextArgC dwReturnValue = CopyArgIntoBuffer(Buffer, CmdLineArgs.ArgV[CmdLineArgs.NextArgC++], BufferSize); } // if (RespFile != INVALID_HANDLE_VALUE) {} else { } else { // if (CmdLineArgs.ArgV[CmdLineArgs.NextArgC][0] == '@') { // not a response file if (fVerbose) fprintf(stderr,"BINPLACE : warning GNA0293: Not a response file (%s)\n",CmdLineArgs.ArgV[CmdLineArgs.NextArgC]); // fill in the required size if (RequiredSize != NULL) { *RequiredSize = _tcsclen(CmdLineArgs.ArgV[CmdLineArgs.NextArgC])+1; } // no, so fill in Buffer and advanced NextArgC dwReturnValue = CopyArgIntoBuffer(Buffer, CmdLineArgs.ArgV[CmdLineArgs.NextArgC++], BufferSize); } // if (CmdLineArgs.ArgV[CmdLineArgs.NextArgC][0] == '@') {} else { } // if (CmdLineArgs.NextArgC >= CmdLineArgs.MaxArgC) {} else { } // if (fInRespFile) {} else { } // if (Buffer == NULL) {} else { return(dwReturnValue); } /* ------------------------------------------------------------------------------------------------ CopyArgIntoBuffer : does a simple copy with some error checking ------------------------------------------------------------------------------------------------ */ DWORD CopyArgIntoBuffer(TCHAR* Dst, TCHAR* Src, INT DstSize) { HRESULT hrCopyReturn = StringCchCopy(Dst, DstSize, Src); // if the buffer was too small, set a memory error if (HRESULT_CODE(hrCopyReturn) == ERROR_INSUFFICIENT_BUFFER) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } // return actual characters copied to Dst + \0 return(_tcsclen(Dst)+1); }