argv.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /**
  2. * WinPR: Windows Portable Runtime
  3. * Process Argument Vector Functions
  4. *
  5. * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include "config.h"
  21. #endif
  22. #include <winpr/crt.h>
  23. #include <winpr/heap.h>
  24. #include <winpr/handle.h>
  25. #include <winpr/thread.h>
  26. #ifdef HAVE_UNISTD_H
  27. #include <unistd.h>
  28. #endif
  29. #include "../log.h"
  30. #define TAG WINPR_TAG("thread")
  31. /**
  32. * CommandLineToArgvW function:
  33. * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391/
  34. *
  35. * CommandLineToArgvW has a special interpretation of backslash characters
  36. * when they are followed by a quotation mark character ("), as follows:
  37. *
  38. * 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.
  39. * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a
  40. * quotation mark. n backslashes not followed by a quotation mark simply produce n backslashes.
  41. *
  42. * The address returned by CommandLineToArgvW is the address of the first element in an array of
  43. * LPWSTR values; the number of pointers in this array is indicated by pNumArgs. Each pointer to a
  44. * null-terminated Unicode string represents an individual argument found on the command line.
  45. *
  46. * CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings,
  47. * and for the argument strings themselves; the calling application must free the memory used by the
  48. * argument list when it is no longer needed. To free the memory, use a single call to the LocalFree
  49. * function.
  50. */
  51. /**
  52. * Parsing C++ Command-Line Arguments:
  53. * http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft
  54. *
  55. * Microsoft C/C++ startup code uses the following rules when
  56. * interpreting arguments given on the operating system command line:
  57. *
  58. * Arguments are delimited by white space, which is either a space or a tab.
  59. *
  60. * The caret character (^) is not recognized as an escape character or delimiter.
  61. * The character is handled completely by the command-line parser in the operating
  62. * system before being passed to the argv array in the program.
  63. *
  64. * A string surrounded by double quotation marks ("string") is interpreted as a
  65. * single argument, regardless of white space contained within. A quoted string
  66. * can be embedded in an argument.
  67. *
  68. * A double quotation mark preceded by a backslash (\") is interpreted as a
  69. * literal double quotation mark character (").
  70. *
  71. * Backslashes are interpreted literally, unless they immediately
  72. * precede a double quotation mark.
  73. *
  74. * If an even number of backslashes is followed by a double quotation mark,
  75. * one backslash is placed in the argv array for every pair of backslashes,
  76. * and the double quotation mark is interpreted as a string delimiter.
  77. *
  78. * If an odd number of backslashes is followed by a double quotation mark,
  79. * one backslash is placed in the argv array for every pair of backslashes,
  80. * and the double quotation mark is "escaped" by the remaining backslash,
  81. * causing a literal double quotation mark (") to be placed in argv.
  82. *
  83. */
  84. LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs)
  85. {
  86. char* p;
  87. size_t length;
  88. char* pBeg;
  89. char* pEnd;
  90. char* buffer;
  91. char* pOutput;
  92. int numArgs = 0;
  93. LPSTR* pArgs;
  94. size_t maxNumArgs;
  95. size_t maxBufferSize;
  96. size_t cmdLineLength;
  97. BOOL* lpEscapedChars;
  98. LPSTR lpEscapedCmdLine;
  99. if (!lpCmdLine)
  100. return NULL;
  101. if (!pNumArgs)
  102. return NULL;
  103. pArgs = NULL;
  104. lpEscapedCmdLine = NULL;
  105. cmdLineLength = strlen(lpCmdLine);
  106. lpEscapedChars = (BOOL*)calloc(cmdLineLength + 1, sizeof(BOOL));
  107. if (!lpEscapedChars)
  108. return NULL;
  109. if (strstr(lpCmdLine, "\\\""))
  110. {
  111. size_t i;
  112. size_t n;
  113. char* pLastEnd = NULL;
  114. lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char));
  115. if (!lpEscapedCmdLine)
  116. {
  117. free(lpEscapedChars);
  118. return NULL;
  119. }
  120. p = (char*)lpCmdLine;
  121. pLastEnd = (char*)lpCmdLine;
  122. pOutput = (char*)lpEscapedCmdLine;
  123. while (p < &lpCmdLine[cmdLineLength])
  124. {
  125. pBeg = strstr(p, "\\\"");
  126. if (!pBeg)
  127. {
  128. length = strlen(p);
  129. CopyMemory(pOutput, p, length);
  130. pOutput += length;
  131. break;
  132. }
  133. pEnd = pBeg + 2;
  134. while (pBeg >= lpCmdLine)
  135. {
  136. if (*pBeg != '\\')
  137. {
  138. pBeg++;
  139. break;
  140. }
  141. pBeg--;
  142. }
  143. n = ((pEnd - pBeg) - 1);
  144. length = (pBeg - pLastEnd);
  145. CopyMemory(pOutput, p, length);
  146. pOutput += length;
  147. p += length;
  148. for (i = 0; i < (n / 2); i++)
  149. *pOutput++ = '\\';
  150. p += n + 1;
  151. if ((n % 2) != 0)
  152. lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE;
  153. *pOutput++ = '"';
  154. pLastEnd = p;
  155. }
  156. *pOutput++ = '\0';
  157. lpCmdLine = (LPCSTR)lpEscapedCmdLine;
  158. cmdLineLength = strlen(lpCmdLine);
  159. }
  160. maxNumArgs = 2;
  161. p = (char*)lpCmdLine;
  162. while (p < lpCmdLine + cmdLineLength)
  163. {
  164. p += strcspn(p, " \t");
  165. p += strspn(p, " \t");
  166. maxNumArgs++;
  167. }
  168. maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
  169. buffer = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, maxBufferSize);
  170. if (!buffer)
  171. {
  172. free(lpEscapedCmdLine);
  173. free(lpEscapedChars);
  174. return NULL;
  175. }
  176. pArgs = (LPSTR*)buffer;
  177. pOutput = (char*)&buffer[maxNumArgs * (sizeof(char*))];
  178. p = (char*)lpCmdLine;
  179. while (p < lpCmdLine + cmdLineLength)
  180. {
  181. pBeg = p;
  182. while (1)
  183. {
  184. p += strcspn(p, " \t\"\0");
  185. if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
  186. break;
  187. p++;
  188. }
  189. if (*p != '"')
  190. {
  191. /* no whitespace escaped with double quotes */
  192. length = (p - pBeg);
  193. CopyMemory(pOutput, pBeg, length);
  194. pOutput[length] = '\0';
  195. pArgs[numArgs++] = pOutput;
  196. pOutput += (length + 1);
  197. }
  198. else
  199. {
  200. p++;
  201. while (1)
  202. {
  203. p += strcspn(p, "\"\0");
  204. if ((*p != '"') || !lpEscapedChars[p - lpCmdLine])
  205. break;
  206. p++;
  207. }
  208. if (*p != '"')
  209. WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!");
  210. if (*p && *(++p))
  211. p += strcspn(p, " \t\0");
  212. pArgs[numArgs++] = pOutput;
  213. while (pBeg < p)
  214. {
  215. if (*pBeg != '"')
  216. *pOutput++ = *pBeg;
  217. pBeg++;
  218. }
  219. *pOutput++ = '\0';
  220. }
  221. p += strspn(p, " \t");
  222. }
  223. free(lpEscapedCmdLine);
  224. free(lpEscapedChars);
  225. *pNumArgs = numArgs;
  226. return pArgs;
  227. }
  228. #ifndef _WIN32
  229. LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs)
  230. {
  231. return NULL;
  232. }
  233. #endif