2020-09-30 16:53:55 +02:00

342 lines
14 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <GetNextArg.h>
#include <strsafe.h>
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);
}