win32_init.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License 2.0 (the "License"). You may not use
  5. * this file except in compliance with the License. You can obtain a copy
  6. * in the file LICENSE in the source distribution or at
  7. * https://www.openssl.org/source/license.html
  8. */
  9. #include <windows.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <malloc.h>
  13. #if defined(CP_UTF8)
  14. static UINT saved_cp;
  15. static int newargc;
  16. static char **newargv;
  17. static void cleanup(void)
  18. {
  19. int i;
  20. SetConsoleOutputCP(saved_cp);
  21. for (i = 0; i < newargc; i++)
  22. free(newargv[i]);
  23. free(newargv);
  24. }
  25. /*
  26. * Incrementally [re]allocate newargv and keep it NULL-terminated.
  27. */
  28. static int validate_argv(int argc)
  29. {
  30. static int size = 0;
  31. if (argc >= size) {
  32. char **ptr;
  33. while (argc >= size)
  34. size += 64;
  35. ptr = realloc(newargv, size * sizeof(newargv[0]));
  36. if (ptr == NULL)
  37. return 0;
  38. (newargv = ptr)[argc] = NULL;
  39. } else {
  40. newargv[argc] = NULL;
  41. }
  42. return 1;
  43. }
  44. static int process_glob(WCHAR *wstr, int wlen)
  45. {
  46. int i, slash, udlen;
  47. WCHAR saved_char;
  48. WIN32_FIND_DATAW data;
  49. HANDLE h;
  50. /*
  51. * Note that we support wildcard characters only in filename part
  52. * of the path, and not in directories. Windows users are used to
  53. * this, that's why recursive glob processing is not implemented.
  54. */
  55. /*
  56. * Start by looking for last slash or backslash, ...
  57. */
  58. for (slash = 0, i = 0; i < wlen; i++)
  59. if (wstr[i] == L'/' || wstr[i] == L'\\')
  60. slash = i + 1;
  61. /*
  62. * ... then look for asterisk or question mark in the file name.
  63. */
  64. for (i = slash; i < wlen; i++)
  65. if (wstr[i] == L'*' || wstr[i] == L'?')
  66. break;
  67. if (i == wlen)
  68. return 0; /* definitely not a glob */
  69. saved_char = wstr[wlen];
  70. wstr[wlen] = L'\0';
  71. h = FindFirstFileW(wstr, &data);
  72. wstr[wlen] = saved_char;
  73. if (h == INVALID_HANDLE_VALUE)
  74. return 0; /* not a valid glob, just pass... */
  75. if (slash)
  76. udlen = WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
  77. NULL, 0, NULL, NULL);
  78. else
  79. udlen = 0;
  80. do {
  81. int uflen;
  82. char *arg;
  83. /*
  84. * skip over . and ..
  85. */
  86. if (data.cFileName[0] == L'.') {
  87. if ((data.cFileName[1] == L'\0') ||
  88. (data.cFileName[1] == L'.' && data.cFileName[2] == L'\0'))
  89. continue;
  90. }
  91. if (!validate_argv(newargc + 1))
  92. break;
  93. /*
  94. * -1 below means "scan for trailing '\0' *and* count it",
  95. * so that |uflen| covers even trailing '\0'.
  96. */
  97. uflen = WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
  98. NULL, 0, NULL, NULL);
  99. arg = malloc(udlen + uflen);
  100. if (arg == NULL)
  101. break;
  102. if (udlen)
  103. WideCharToMultiByte(CP_UTF8, 0, wstr, slash,
  104. arg, udlen, NULL, NULL);
  105. WideCharToMultiByte(CP_UTF8, 0, data.cFileName, -1,
  106. arg + udlen, uflen, NULL, NULL);
  107. newargv[newargc++] = arg;
  108. } while (FindNextFileW(h, &data));
  109. CloseHandle(h);
  110. return 1;
  111. }
  112. void win32_utf8argv(int *argc, char **argv[])
  113. {
  114. const WCHAR *wcmdline;
  115. WCHAR *warg, *wend, *p;
  116. int wlen, ulen, valid = 1;
  117. char *arg;
  118. if (GetEnvironmentVariableW(L"OPENSSL_WIN32_UTF8", NULL, 0) == 0)
  119. return;
  120. newargc = 0;
  121. newargv = NULL;
  122. if (!validate_argv(newargc))
  123. return;
  124. wcmdline = GetCommandLineW();
  125. if (wcmdline == NULL) return;
  126. /*
  127. * make a copy of the command line, since we might have to modify it...
  128. */
  129. wlen = wcslen(wcmdline);
  130. p = _alloca((wlen + 1) * sizeof(WCHAR));
  131. wcscpy(p, wcmdline);
  132. while (*p != L'\0') {
  133. int in_quote = 0;
  134. if (*p == L' ' || *p == L'\t') {
  135. p++; /* skip over white spaces */
  136. continue;
  137. }
  138. /*
  139. * Note: because we may need to fiddle with the number of backslashes,
  140. * the argument string is copied into itself. This is safe because
  141. * the number of characters will never expand.
  142. */
  143. warg = wend = p;
  144. while (*p != L'\0'
  145. && (in_quote || (*p != L' ' && *p != L'\t'))) {
  146. switch (*p) {
  147. case L'\\':
  148. /*
  149. * Microsoft documentation on how backslashes are treated
  150. * is:
  151. *
  152. * + Backslashes are interpreted literally, unless they
  153. * immediately precede a double quotation mark.
  154. * + If an even number of backslashes is followed by a double
  155. * quotation mark, one backslash is placed in the argv array
  156. * for every pair of backslashes, and the double quotation
  157. * mark is interpreted as a string delimiter.
  158. * + If an odd number of backslashes is followed by a double
  159. * quotation mark, one backslash is placed in the argv array
  160. * for every pair of backslashes, and the double quotation
  161. * mark is "escaped" by the remaining backslash, causing a
  162. * literal double quotation mark (") to be placed in argv.
  163. *
  164. * Ref: https://msdn.microsoft.com/en-us/library/17w5ykft.aspx
  165. *
  166. * Though referred page doesn't mention it, multiple qouble
  167. * quotes are also special. Pair of double quotes in quoted
  168. * string is counted as single double quote.
  169. */
  170. {
  171. const WCHAR *q = p;
  172. int i;
  173. while (*p == L'\\')
  174. p++;
  175. if (*p == L'"') {
  176. int i;
  177. for (i = (p - q) / 2; i > 0; i--)
  178. *wend++ = L'\\';
  179. /*
  180. * if odd amount of backslashes before the quote,
  181. * said quote is part of the argument, not a delimiter
  182. */
  183. if ((p - q) % 2 == 1)
  184. *wend++ = *p++;
  185. } else {
  186. for (i = p - q; i > 0; i--)
  187. *wend++ = L'\\';
  188. }
  189. }
  190. break;
  191. case L'"':
  192. /*
  193. * Without the preceding backslash (or when preceded with an
  194. * even number of backslashes), the double quote is a simple
  195. * string delimiter and just slightly change the parsing state
  196. */
  197. if (in_quote && p[1] == L'"')
  198. *wend++ = *p++;
  199. else
  200. in_quote = !in_quote;
  201. p++;
  202. break;
  203. default:
  204. /*
  205. * Any other non-delimiter character is just taken verbatim
  206. */
  207. *wend++ = *p++;
  208. }
  209. }
  210. wlen = wend - warg;
  211. if (wlen == 0 || !process_glob(warg, wlen)) {
  212. if (!validate_argv(newargc + 1)) {
  213. valid = 0;
  214. break;
  215. }
  216. ulen = 0;
  217. if (wlen > 0) {
  218. ulen = WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
  219. NULL, 0, NULL, NULL);
  220. if (ulen <= 0)
  221. continue;
  222. }
  223. arg = malloc(ulen + 1);
  224. if (arg == NULL) {
  225. valid = 0;
  226. break;
  227. }
  228. if (wlen > 0)
  229. WideCharToMultiByte(CP_UTF8, 0, warg, wlen,
  230. arg, ulen, NULL, NULL);
  231. arg[ulen] = '\0';
  232. newargv[newargc++] = arg;
  233. }
  234. }
  235. if (valid) {
  236. saved_cp = GetConsoleOutputCP();
  237. SetConsoleOutputCP(CP_UTF8);
  238. *argc = newargc;
  239. *argv = newargv;
  240. atexit(cleanup);
  241. } else if (newargv != NULL) {
  242. int i;
  243. for (i = 0; i < newargc; i++)
  244. free(newargv[i]);
  245. free(newargv);
  246. newargc = 0;
  247. newargv = NULL;
  248. }
  249. return;
  250. }
  251. #else
  252. void win32_utf8argv(int *argc, char **argv[])
  253. { return; }
  254. #endif