Leaked source code of windows server 2003
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.

341 lines
14 KiB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <malloc.h>
  4. #include <GetNextArg.h>
  5. #include <strsafe.h>
  6. extern BOOL fVerbose; // linked in from binplace.c
  7. // structure for recording current state of
  8. // an argument vector
  9. typedef struct _ARG_INFO {
  10. TCHAR** ArgV;
  11. INT MaxArgC;
  12. INT NextArgC;
  13. } ARG_INFO;
  14. // global variables for same
  15. static ARG_INFO RespFileArgs;
  16. static ARG_INFO CmdLineArgs;
  17. // flag to let us know if we're currently in a response
  18. // file or not. Initially, we're not so init to FALSE
  19. static BOOL fInRespFile = FALSE;
  20. // flag to let us know if this is the first call to GetNextArg()
  21. static BOOL fFirstCall = TRUE;
  22. // CRT global used by _setargv()
  23. _CRTIMP extern char *_acmdln;
  24. // struct for __getmainargs()
  25. typedef struct { int newmode; } _startupinfo;
  26. // decl for _getmainargs() - expands _acmdln to (__argc, __argv)
  27. _CRTIMP int __cdecl __getmainargs (int *pargc, char ***pargv, char ***penvp, int dowildcard, _startupinfo * startinfo);
  28. // local functions
  29. DWORD CopyArgIntoBuffer(TCHAR* Dst, TCHAR* Src, INT DstSize);
  30. /* ------------------------------------------------------------------------------------------------
  31. GetNextArgSize : returns the size required to hold the next argument
  32. ** SEE ASSUMPTIONS AND LIMITATIONS REGARDING GetNextArg() BELOW **
  33. ------------------------------------------------------------------------------------------------ */
  34. DWORD GetNextArgSize(void) {
  35. TCHAR TempBuffer[1]; // minimal buffer to pass to GetNextArg
  36. DWORD dwNextRequiredSize = 0; // return value
  37. DWORD dwTempValue; // temp DWORD for GetNextArg() return value
  38. // instead of mimicing GetNextArg, we'll just call it then rollback the correct globals
  39. dwTempValue = GetNextArg(TempBuffer, 1, &dwNextRequiredSize);
  40. // roll back the relevent global
  41. if (fInRespFile) {
  42. RespFileArgs.NextArgC--;
  43. } else {
  44. CmdLineArgs.NextArgC--;
  45. }
  46. return(dwNextRequiredSize);
  47. }
  48. /* ------------------------------------------------------------------------------------------------
  49. GetNextArg : returns next argument in array including opening and expending reponse files
  50. given on the command line
  51. Buffer is the buffer to copy the next argument into
  52. BufferSize is the size of Buffer in characters
  53. RequiredSize, if not NULL, is set to the actual size required for the parameter (inluding '\0').
  54. This is useful for determining whether a return value of 0 is an error or end of arguments.
  55. Return value is the number of characters copied including \0.
  56. if return value is 0 and RequiredSize is non-zero, an error occurred - check GetLastError()
  57. if return value is 0 and RequiredSize is zero, no more arguments are available
  58. Assumptions: the calling program does not modify _acmdln, __argv, __argc
  59. the calling program ignores main's (int, char**)
  60. Limitations: does not work with UNICODE response files (not tested when built w/ -D_UNICODE)
  61. response files may not include response files
  62. literal parameter beginning with '@' is returned if:
  63. - parameter is found in a response file --OR--
  64. - the named file cannot be opened --OR--
  65. - the named file won't fit in memory
  66. environment strings in repsonse file are not expanded
  67. ------------------------------------------------------------------------------------------------ */
  68. DWORD GetNextArg(OUT TCHAR* Buffer, IN DWORD BufferSize, OPTIONAL OUT DWORD* RequiredSize) {
  69. DWORD dwReturnValue; // value to return
  70. HANDLE* RespFile; // for reading in a new response file
  71. TCHAR* TempBuffer;
  72. TCHAR* pTchar;
  73. TCHAR* pBeginBuffer;
  74. DWORD dwSize;
  75. // first-call initialization
  76. if (fFirstCall) {
  77. CmdLineArgs.ArgV = __argv; // set to initial __argv
  78. CmdLineArgs.MaxArgC = __argc; // set to initial __argc
  79. CmdLineArgs.NextArgC = 0; // no args used yet
  80. RespFileArgs.ArgV = NULL;
  81. RespFileArgs.MaxArgC = 0;
  82. RespFileArgs.NextArgC = -1;
  83. fFirstCall = FALSE;
  84. }
  85. // buffer cannot be null
  86. if (Buffer == NULL) {
  87. SetLastError(ERROR_INVALID_PARAMETER);
  88. dwReturnValue = 0;
  89. if (fVerbose)
  90. fprintf(stderr,"BINPLACE : warning GNA0113: Passed NULL buffer\n");
  91. } else {
  92. // handle getting next arg when reading a response file
  93. if (fInRespFile) {
  94. // was the previous arg the last one from the file
  95. if (RespFileArgs.NextArgC >= RespFileArgs.MaxArgC) {
  96. if (fVerbose)
  97. fprintf(stderr,"BINPLACE : warning GNA0127: Response file finished\n");
  98. // yes, so clear the flag and skip to reading the next value from the cmdline
  99. fInRespFile = FALSE;
  100. goto UseArgV;
  101. } else {
  102. // fill in the required size
  103. if (RequiredSize != NULL) {
  104. *RequiredSize = _tcsclen(RespFileArgs.ArgV[RespFileArgs.NextArgC])+1;
  105. }
  106. // no, so fill in Buffer and advanced NextArgC
  107. dwReturnValue = CopyArgIntoBuffer(Buffer, RespFileArgs.ArgV[RespFileArgs.NextArgC++], BufferSize);
  108. }
  109. // handle getting next cmdline arg
  110. } else {
  111. UseArgV:
  112. // was the previous arg the last one?
  113. if (CmdLineArgs.NextArgC >= CmdLineArgs.MaxArgC) {
  114. // yes, so set safe return values
  115. if (RequiredSize != NULL) {
  116. *RequiredSize = 0;
  117. }
  118. Buffer[0] = TEXT('\0');
  119. dwReturnValue = 0;
  120. if (fVerbose)
  121. fprintf(stderr,"BINPLACE : warning GNA0127: Command line finished\n");
  122. } else {
  123. // no, so ge the next arg
  124. // is the next arg a response file?
  125. if (CmdLineArgs.ArgV[CmdLineArgs.NextArgC][0] == '@') {
  126. // yes, try to open it
  127. RespFile=CreateFile((CmdLineArgs.ArgV[CmdLineArgs.NextArgC]+1), // don't include '@'
  128. GENERIC_READ, 0, NULL,
  129. OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,(HANDLE)NULL);
  130. if (RespFile != INVALID_HANDLE_VALUE) {
  131. if (fVerbose)
  132. fprintf(stderr,"BINPLACE : warning GNA0174: Using response file: %s \n",(CmdLineArgs.ArgV[CmdLineArgs.NextArgC]+1));
  133. // advance CmdLineArgs.NextArgC so we don't try to load the file again as
  134. // soon as it's finished
  135. CmdLineArgs.NextArgC++;
  136. // file opened - get the size required to load it into memory
  137. dwSize=GetFileSize(RespFile,NULL);
  138. // try to get enough memory to load the file
  139. TempBuffer = (TCHAR*)malloc(sizeof(TCHAR)*(dwSize+1));
  140. if (TempBuffer != NULL) {
  141. // store a pointer to the start of the buffer
  142. pBeginBuffer = TempBuffer;
  143. // zero the memory then load the file
  144. ZeroMemory(TempBuffer, _msize(TempBuffer));
  145. ReadFile(RespFile,TempBuffer, _msize(TempBuffer),&dwSize,NULL);
  146. // ensure NULL termination
  147. TempBuffer[dwSize]='\0';
  148. // map \r and \n to spaces because _setargv won't do it for us
  149. while (pTchar=strchr(TempBuffer,'\r')) {
  150. *pTchar = ' ';
  151. pTchar++;
  152. if (*pTchar == '\n') {
  153. *pTchar = ' ';
  154. }
  155. }
  156. // setargv dislikes odd leading characters, so step past them
  157. while ( ( (!isprint(TempBuffer[0])) ||
  158. ( isspace(TempBuffer[0])) ) &&
  159. ( TempBuffer[0] != 0 ) ) {
  160. TempBuffer++;
  161. }
  162. // how to handle this case? response file existed but contained no data??
  163. if (_tcsclen(TempBuffer) > 0) {
  164. // required for call to getmainargs()
  165. _startupinfo SInfo = {0};
  166. CHAR** Unused;
  167. // _setargv() expects _acmdln to point to the string to convert
  168. _acmdln = TempBuffer;
  169. // actually convert the string
  170. if ( __getmainargs(&RespFileArgs.MaxArgC, &RespFileArgs.ArgV, &Unused, 1, &SInfo) < 0 ) {
  171. if (fVerbose)
  172. fprintf(stderr,"BINPLACE : warning GNA0230: Failed to get args from response file- skipping it\n");
  173. goto UseArgV;
  174. }
  175. // clean up temp resources
  176. free(pBeginBuffer);
  177. CloseHandle(RespFile);
  178. // init the global structure
  179. RespFileArgs.NextArgC = 0;
  180. fInRespFile = TRUE;
  181. // fill in the required size
  182. if (RequiredSize != NULL) {
  183. *RequiredSize = _tcsclen(RespFileArgs.ArgV[RespFileArgs.NextArgC])+1;
  184. }
  185. // fill in Buffer
  186. dwReturnValue = CopyArgIntoBuffer(Buffer, RespFileArgs.ArgV[RespFileArgs.NextArgC++], BufferSize);
  187. } else { // file contains no parameters
  188. if (fVerbose)
  189. fprintf(stderr,"BINPLACE : warning GNA0253: Empty response file- ignoring\n");
  190. // clean up temp resources
  191. free(pBeginBuffer);
  192. CloseHandle(RespFile);
  193. // Instead of returning the literal filename, skip over the file and just return the next arg
  194. goto UseArgV;
  195. // // fill in the required size
  196. // if (RequiredSize != NULL) {
  197. // *RequiredSize = _tcsclen(CmdLineArgs.ArgV[CmdLineArgs.NextArgC-1])+1;
  198. // }
  199. //
  200. // // fill in Buffer and advanced NextArgC
  201. // dwReturnValue = CopyArgIntoBuffer(Buffer, CmdLineArgs.ArgV[CmdLineArgs.NextArgC-1], BufferSize);
  202. }
  203. } else { // not enough memory to load the file- not sure what the best way to handle this is
  204. if (fVerbose)
  205. fprintf(stderr,"BINPLACE : warning GNA0272: Out of memory\n");
  206. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  207. if (RequiredSize != NULL) {
  208. *RequiredSize = 1; // set to non-zero value
  209. }
  210. Buffer[0] = TEXT('\0');
  211. dwReturnValue = 0;
  212. }
  213. } else { // it *looked* like a response file, but couldn't be opened, so return it literally
  214. if (fVerbose)
  215. fprintf(stderr,"BINPLACE : warning GNA0277: Can't open response file %s\n",(CmdLineArgs.ArgV[CmdLineArgs.NextArgC]+1));
  216. // fill in the required size
  217. if (RequiredSize != NULL) {
  218. *RequiredSize = _tcsclen(CmdLineArgs.ArgV[CmdLineArgs.NextArgC])+1;
  219. }
  220. // fill in Buffer and advanced NextArgC
  221. dwReturnValue = CopyArgIntoBuffer(Buffer, CmdLineArgs.ArgV[CmdLineArgs.NextArgC++], BufferSize);
  222. } // if (RespFile != INVALID_HANDLE_VALUE) {} else {
  223. } else { // if (CmdLineArgs.ArgV[CmdLineArgs.NextArgC][0] == '@') {
  224. // not a response file
  225. if (fVerbose)
  226. fprintf(stderr,"BINPLACE : warning GNA0293: Not a response file (%s)\n",CmdLineArgs.ArgV[CmdLineArgs.NextArgC]);
  227. // fill in the required size
  228. if (RequiredSize != NULL) {
  229. *RequiredSize = _tcsclen(CmdLineArgs.ArgV[CmdLineArgs.NextArgC])+1;
  230. }
  231. // no, so fill in Buffer and advanced NextArgC
  232. dwReturnValue = CopyArgIntoBuffer(Buffer, CmdLineArgs.ArgV[CmdLineArgs.NextArgC++], BufferSize);
  233. } // if (CmdLineArgs.ArgV[CmdLineArgs.NextArgC][0] == '@') {} else {
  234. } // if (CmdLineArgs.NextArgC >= CmdLineArgs.MaxArgC) {} else {
  235. } // if (fInRespFile) {} else {
  236. } // if (Buffer == NULL) {} else {
  237. return(dwReturnValue);
  238. }
  239. /* ------------------------------------------------------------------------------------------------
  240. CopyArgIntoBuffer : does a simple copy with some error checking
  241. ------------------------------------------------------------------------------------------------ */
  242. DWORD CopyArgIntoBuffer(TCHAR* Dst, TCHAR* Src, INT DstSize) {
  243. HRESULT hrCopyReturn = StringCchCopy(Dst, DstSize, Src);
  244. // if the buffer was too small, set a memory error
  245. if (HRESULT_CODE(hrCopyReturn) == ERROR_INSUFFICIENT_BUFFER) {
  246. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  247. }
  248. // return actual characters copied to Dst + \0
  249. return(_tcsclen(Dst)+1);
  250. }