PageRenderTime 53ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/wrapmsvc.c

http://wrapmsvc.googlecode.com/
C | 869 lines | 682 code | 102 blank | 85 comment | 208 complexity | 5ea79311b670e164333643a7419c1280 MD5 | raw file
  1. /*
  2. Wrappers for running the MSVC compiler and related tools through Wine
  3. Copyright 2009 Ambroz Bizjak <ambrop7@gmail.com>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License version 2
  6. as published by the Free Software Foundation.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License along
  12. with this program; if not, write to the Free Software Foundation, Inc.,
  13. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  14. */
  15. #include <windows.h>
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <ctype.h>
  20. #include <stdint.h>
  21. #include <assert.h>
  22. #include <inttypes.h>
  23. #include <unistd.h>
  24. #include "debug.h"
  25. #include "Reactor.h"
  26. #define MAX_CMDLINE_LEN 65536
  27. #define MT_MAGIC 1090650113
  28. #define MT_MAGIC_CONV 187
  29. #define WCHAR_CONVERT_CP CP_UNIXCP
  30. #if defined(WRAP_CL)
  31. #define CMD_ENVVAR "CL_CMD"
  32. #elif defined(WRAP_LINK)
  33. #define CMD_ENVVAR "LINK_CMD"
  34. #elif defined(WRAP_RC)
  35. #define CMD_ENVVAR "RC_CMD"
  36. #elif defined(WRAP_MT)
  37. #define CMD_ENVVAR "MT_CMD"
  38. #else
  39. #error UNKNOWN PROGRAM TO WRAP
  40. #endif
  41. #define CMDLINE_BUF_LEN (MAX_CMDLINE_LEN + 1)
  42. static void fail (char *msg)
  43. {
  44. printf("WRAPPER ERROR: %s\n", msg);
  45. exit(1);
  46. }
  47. static int begins_with (const char *str, const char *needle, int *len)
  48. {
  49. int i;
  50. for (i = 0; ; i++) {
  51. if (needle[i] == '\0') {
  52. if (len) {
  53. *len = i;
  54. }
  55. return 1;
  56. }
  57. if (str[i] != needle[i]) {
  58. return 0;
  59. }
  60. }
  61. }
  62. static int begins_with_ci (const char *str, const char *needle, int *len)
  63. {
  64. int i;
  65. for (i = 0; ; i++) {
  66. if (needle[i] == '\0') {
  67. if (len) {
  68. *len = i;
  69. }
  70. return 1;
  71. }
  72. if (toupper(str[i]) != toupper(needle[i])) {
  73. return 0;
  74. }
  75. }
  76. }
  77. void print_a (char *str, int len)
  78. {
  79. if (len < 0) {
  80. len = strlen(str);
  81. }
  82. write(2, str, len);
  83. }
  84. void print_w (WCHAR *w, int len)
  85. {
  86. if (len < 0) {
  87. len = wcslen(w);
  88. }
  89. if (len == 0) {
  90. return;
  91. }
  92. int alen = WideCharToMultiByte(CP_UNIXCP, 0, w, len, NULL, 0, NULL, NULL);
  93. if (alen == 0) {
  94. fail("WideCharToMultiByte failed");
  95. }
  96. char *a = HeapAlloc(GetProcessHeap(), 0, alen * sizeof(char));
  97. if (!a) {
  98. fail("HeapAlloc failed");
  99. }
  100. WideCharToMultiByte(CP_UNIXCP, 0, w, len, a, alen, NULL, NULL);
  101. write(2, a, alen);
  102. HeapFree(GetProcessHeap(), 0, a);
  103. }
  104. Reactor r;
  105. int return_value;
  106. PROCESS_INFORMATION pi;
  107. // Handler called by the reactor when the process terminates
  108. void process_handler (void *unused)
  109. {
  110. DWORD exit_code;
  111. if (!GetExitCodeProcess(pi.hProcess, &exit_code)) {
  112. fail("GetExitCodeProcess failed");
  113. }
  114. #if defined(WRAP_MT)
  115. if (exit_code == MT_MAGIC) {
  116. exit_code = MT_MAGIC_CONV;
  117. }
  118. #endif
  119. CloseHandle(pi.hProcess);
  120. CloseHandle(pi.hThread);
  121. return_value = exit_code;
  122. Reactor_Quit(&r);
  123. }
  124. int main (int argc, char **argv)
  125. {
  126. char *cl_cmd = getenv(CMD_ENVVAR);
  127. if (!cl_cmd) {
  128. fail(CMD_ENVVAR" not set");
  129. }
  130. #ifndef WINTEST
  131. LPWSTR (*CDECL wine_get_dos_file_name_ptr)(LPCSTR);
  132. wine_get_dos_file_name_ptr = (void *)GetProcAddress(GetModuleHandleA("KERNEL32"), "wine_get_dos_file_name");
  133. if (!wine_get_dos_file_name_ptr) {
  134. fail("cannot get wine_get_dos_file_name");
  135. }
  136. #endif
  137. WCHAR cmdline[CMDLINE_BUF_LEN];
  138. int cmdline_len = 0;
  139. void append_char (WCHAR c)
  140. {
  141. if (1 > MAX_CMDLINE_LEN - cmdline_len) {
  142. fail("out of command line buffer");
  143. }
  144. cmdline[cmdline_len] = c;
  145. cmdline_len++;
  146. }
  147. int arg_writing = 0;
  148. void start_argument ()
  149. {
  150. ASSERT(!arg_writing)
  151. if (cmdline_len == 0) {
  152. append_char('"');
  153. } else {
  154. append_char(' ');
  155. append_char('"');
  156. }
  157. arg_writing = 1;
  158. }
  159. void write_argument_w (WCHAR *data, int len)
  160. {
  161. ASSERT(arg_writing)
  162. ASSERT(len >= 0)
  163. int i;
  164. for (i = 0; i < len; i++) {
  165. if (data[i] == '"') {
  166. int j;
  167. for (j = cmdline_len - 1; cmdline[j] == '\\'; j--) {
  168. append_char('\\');
  169. }
  170. append_char('\\');
  171. append_char(data[i]);
  172. } else {
  173. append_char(data[i]);
  174. }
  175. }
  176. }
  177. void end_argument ()
  178. {
  179. ASSERT(arg_writing)
  180. int j;
  181. for (j = cmdline_len - 1; cmdline[j] == '\\'; j--) {
  182. append_char('\\');
  183. }
  184. append_char('"');
  185. arg_writing = 0;
  186. }
  187. void write_argument_a (char *data, int len)
  188. {
  189. ASSERT(arg_writing)
  190. ASSERT(len >= 0)
  191. if (len == 0) {
  192. return;
  193. }
  194. int wc_len = MultiByteToWideChar(WCHAR_CONVERT_CP, 0, data, len, NULL, 0);
  195. if (wc_len == 0) {
  196. fail("MultiByteToWideChar failed");
  197. }
  198. WCHAR *wc = HeapAlloc(GetProcessHeap(), 0, wc_len * sizeof(WCHAR));
  199. if (!wc) {
  200. fail("HeapAlloc failed");
  201. }
  202. MultiByteToWideChar(WCHAR_CONVERT_CP, 0, data, len, wc, wc_len);
  203. write_argument_w(wc, wc_len);
  204. HeapFree(GetProcessHeap(), 0, wc);
  205. }
  206. void write_argument_path (char *path, int len)
  207. {
  208. ASSERT(arg_writing)
  209. ASSERT(len >= 0)
  210. if (len == 0) {
  211. return;
  212. }
  213. // cmake is broken and may give gives unix style paths with backshashes
  214. char *fixed = HeapAlloc(GetProcessHeap(), 0, len + 1);
  215. if (!fixed) {
  216. fail("HeapAlloc failed");
  217. }
  218. int i;
  219. for (i = 0; i < len; i++) {
  220. if (path[i] == '\\') {
  221. fixed[i] = '/';
  222. } else {
  223. fixed[i] = path[i];
  224. }
  225. }
  226. fixed[i] = '\0';
  227. // convert to windows path
  228. WCHAR *conv_path = wine_get_dos_file_name_ptr(fixed);
  229. if (!conv_path) {
  230. fail("cannot convert path");
  231. }
  232. write_argument_w(conv_path, wcslen(conv_path));
  233. HeapFree(GetProcessHeap(), 0, fixed);
  234. HeapFree(GetProcessHeap(), 0, conv_path);
  235. }
  236. // Appends an argument to the command line. Parameters:
  237. // str - zero terminated input argument to be appended
  238. // prefix - all characters of the input argument after
  239. // that many bytes will be treated as a path
  240. void add_arg (char *str, int prefix_len)
  241. {
  242. ASSERT(prefix_len >= 0);
  243. ASSERT(prefix_len <= strlen(str));
  244. int path_len = strlen(str + prefix_len);
  245. start_argument();
  246. write_argument_a(str, prefix_len);
  247. write_argument_path(str + prefix_len, path_len);
  248. end_argument();
  249. }
  250. // parse CL_CMD
  251. char *ch = cl_cmd;
  252. char *current_arg = ch;
  253. while (*ch) {
  254. if (*ch == ';') {
  255. *ch = '\0';
  256. add_arg(current_arg, strlen(current_arg));
  257. current_arg = ch + 1;
  258. }
  259. ch++;
  260. }
  261. add_arg(current_arg, strlen(current_arg));
  262. // Parse arguments.
  263. // Some of this is guessing, because an argument starting with /
  264. // could always be a file rather than an option.
  265. // Handle this so that when given an argument beginning with /,
  266. // and such a file exits on the system, interpret it as a path.
  267. int i = 1;
  268. #if defined(WRAP_CL)
  269. while (i < argc) {
  270. // take argument
  271. char *arg = argv[i];
  272. int len = strlen(arg);
  273. i++;
  274. // parse as option?
  275. if (
  276. len >= 2 && (
  277. arg[0] == '-' ||
  278. (arg[0] == '/' && access(arg, F_OK) < 0)
  279. )
  280. ) {
  281. int optlen;
  282. // arguments with paths
  283. if (len >= 3 && arg[1] == 'F') {
  284. // path optional
  285. if (
  286. arg[2] == 'a' || arg[2] == 'd' || arg[2] == 'm' ||
  287. arg[2] == 'R' || arg[2] == 'r'
  288. ) {
  289. add_arg(arg, 3);
  290. continue;
  291. }
  292. // path required
  293. if (
  294. arg[2] == 'p' || arg[2] == 'e' || arg[2] == 'o' ||
  295. arg[2] == 'I' || arg[2] == 'U'
  296. ) {
  297. add_arg(arg, 3);
  298. // if no path is given, use next argument
  299. if (arg[3] == '\0' && i < argc) {
  300. add_arg(argv[i], 0);
  301. i++;
  302. }
  303. continue;
  304. }
  305. }
  306. // /AI<dir>
  307. if (len >= 3 && arg[1] == 'A' && arg[2] == 'I') {
  308. add_arg(arg, 3);
  309. // if no value is given, use next argument
  310. if (arg[3] == '\0' && i < argc) {
  311. add_arg(argv[i], 0);
  312. i++;
  313. }
  314. continue;
  315. }
  316. // /I<dir>
  317. if (arg[1] == 'I') {
  318. add_arg(arg, 2);
  319. // if no value is given, use next argument
  320. if (arg[2] == '\0' && i < argc) {
  321. add_arg(argv[i], 0);
  322. i++;
  323. }
  324. continue;
  325. }
  326. // /doc[file]
  327. if (len >= 4 && arg[1] == 'd' && arg[2] == 'o' && arg[3] == 'c') {
  328. add_arg(arg, 4);
  329. continue;
  330. }
  331. // /Tc<file>, /Tp<file>
  332. if (
  333. begins_with(arg + 1, "Tc", &optlen) ||
  334. begins_with(arg + 1, "Tp", &optlen)
  335. ) {
  336. add_arg(arg, 1 + optlen);
  337. if (arg[1 + optlen] == '\0' && i < argc) {
  338. add_arg(argv[i], 0);
  339. i++;
  340. }
  341. continue;
  342. }
  343. // /D...
  344. if (arg[1] == 'D') {
  345. add_arg(arg, strlen(arg));
  346. // if no value is given, use next argument
  347. if (arg[2] == '\0' && i < argc) {
  348. add_arg(argv[i], strlen(argv[i]));
  349. i++;
  350. }
  351. continue;
  352. }
  353. // all arguments beginning with this are passed literally
  354. if (
  355. arg[1] == 'O' || arg[1] == 'G' || arg[1] == 'E' || arg[1] == 'Q' ||
  356. arg[1] == 'Z' || arg[1] == 'H' || arg[1] == 'W' || arg[1] == 'w' ||
  357. arg[1] == 'Y' || arg[1] == 'L' || arg[1] == 'M' || arg[1] == 'U'
  358. ) {
  359. add_arg(arg, len);
  360. continue;
  361. }
  362. // /fp:..
  363. if (len >= 4 && !memcmp(arg + 1, "fp:", 3)) {
  364. add_arg(arg, len);
  365. continue;
  366. }
  367. // /RTC...
  368. if (len >= 4 && !memcmp(arg + 1, "RTC", 3)) {
  369. add_arg(arg, len);
  370. continue;
  371. }
  372. // /clr[:option]
  373. if (len >= 4 && !memcmp(arg + 1, "clr", 3)) {
  374. add_arg(arg, len);
  375. continue;
  376. }
  377. // /F<num> set stack size
  378. if (arg[1] == 'F' && len > 2) {
  379. char *endptr;
  380. strtol(arg + 2, &endptr, 10);
  381. if (*endptr == '\0') {
  382. add_arg(arg, len);
  383. continue;
  384. }
  385. }
  386. // flags with no arguments
  387. if (
  388. !strcmp(arg + 1, "showIncludes") ||
  389. !strcmp(arg + 1, "nologo") ||
  390. !strcmp(arg + 1, "bigobj") ||
  391. !strcmp(arg + 1, "help") ||
  392. !strcmp(arg + 1, "?") ||
  393. !strcmp(arg + 1, "openmp") ||
  394. !strcmp(arg + 1, "c") ||
  395. !strcmp(arg + 1, "Fx") ||
  396. !strcmp(arg + 1, "FC") ||
  397. !strcmp(arg + 1, "C") ||
  398. !strcmp(arg + 1, "E") ||
  399. !strcmp(arg + 1, "P") ||
  400. !strcmp(arg + 1, "u") ||
  401. !strcmp(arg + 1, "X") ||
  402. !strcmp(arg + 1, "J") ||
  403. !strcmp(arg + 1, "TC") ||
  404. !strcmp(arg + 1, "TP")
  405. ) {
  406. add_arg(arg, len);
  407. continue;
  408. }
  409. if (!strcmp(arg + 1, "link")) {
  410. add_arg(arg, len);
  411. // linker options follow
  412. break;
  413. }
  414. // assume it's a file
  415. add_arg(arg, 0);
  416. continue;
  417. }
  418. // @<file>
  419. if (len >= 1 && arg[0] == '@') {
  420. add_arg(arg, 1);
  421. continue;
  422. }
  423. // assume it's a file
  424. add_arg(arg, 0);
  425. continue;
  426. }
  427. #endif
  428. #if defined(WRAP_LINK) || defined(WRAP_CL)
  429. while (i < argc) {
  430. // take argument
  431. char *arg = argv[i];
  432. int len = strlen(arg);
  433. i++;
  434. // parse as option?
  435. if (
  436. len >= 2 && (
  437. arg[0] == '-' ||
  438. (arg[0] == '/' && access(arg, F_OK) < 0)
  439. #ifdef WRAP_LINK
  440. || (arg[0] == '/' && !strcasecmp(arg + 1, "lib"))
  441. #endif
  442. )
  443. ) {
  444. int optlen;
  445. // options without arguments
  446. if (
  447. !strcasecmp(arg + 1, "DEBUG") ||
  448. !strcasecmp(arg + 1, "DLL") ||
  449. !strcasecmp(arg + 1, "IGNOREIDL") ||
  450. !strcasecmp(arg + 1, "MAP") ||
  451. !strcasecmp(arg + 1, "NOASSEMBLY") ||
  452. !strcasecmp(arg + 1, "NOENTRY") ||
  453. !strcasecmp(arg + 1, "NOLOGO") ||
  454. !strcasecmp(arg + 1, "PDB:none") ||
  455. !strcasecmp(arg + 1, "PDBSTRIPPED:none") ||
  456. !strcasecmp(arg + 1, "PROFILE") ||
  457. !strcasecmp(arg + 1, "RELEASE")
  458. #ifdef WRAP_LINK
  459. || !strcasecmp(arg + 1, "LIB")
  460. #endif
  461. ) {
  462. add_arg(arg, len);
  463. continue;
  464. }
  465. // options with literal arguments
  466. if (
  467. begins_with_ci(arg + 1, "ALIGN:", &optlen) ||
  468. begins_with_ci(arg + 1, "ALLOWBIND", &optlen) ||
  469. begins_with_ci(arg + 1, "ALLOWISOLATION", &optlen) ||
  470. begins_with_ci(arg + 1, "ASSEMBLYDEBUG", &optlen) ||
  471. begins_with_ci(arg + 1, "ASSEMBLYLINKRESOURCE:", &optlen) || // ?
  472. begins_with_ci(arg + 1, "ASSEMBLYMODULE:", &optlen) || // ?
  473. begins_with_ci(arg + 1, "ASSEMBLYRESOURCE:", &optlen) || // ?
  474. begins_with_ci(arg + 1, "BASE:", &optlen) ||
  475. begins_with_ci(arg + 1, "CLRIMAGETYPE:", &optlen) ||
  476. begins_with_ci(arg + 1, "CLRSUPPORTLASTERROR", &optlen) ||
  477. begins_with_ci(arg + 1, "CLRTHREADATTRIBUTE:", &optlen) ||
  478. begins_with_ci(arg + 1, "CLRUNMANAGEDCODECHECK", &optlen) ||
  479. begins_with_ci(arg + 1, "DEF:", &optlen) ||
  480. begins_with_ci(arg + 1, "DEFAULTLIB:", &optlen) ||
  481. begins_with_ci(arg + 1, "DELAY:", &optlen) ||
  482. begins_with_ci(arg + 1, "DELAYLOAD:", &optlen) ||
  483. begins_with_ci(arg + 1, "DELAYSIGN", &optlen) ||
  484. begins_with_ci(arg + 1, "DRIVER", &optlen) ||
  485. begins_with_ci(arg + 1, "DYNAMICBASE", &optlen) ||
  486. begins_with_ci(arg + 1, "ENTRY:", &optlen) ||
  487. begins_with_ci(arg + 1, "ERRORREPORT:", &optlen) ||
  488. begins_with_ci(arg + 1, "EXPORT:", &optlen) ||
  489. begins_with_ci(arg + 1, "FIXED", &optlen) ||
  490. begins_with_ci(arg + 1, "FORCE", &optlen) ||
  491. begins_with_ci(arg + 1, "FUNCTIONPADMIN", &optlen) ||
  492. begins_with_ci(arg + 1, "HEAP:", &optlen) ||
  493. begins_with_ci(arg + 1, "IDLOUT:", &optlen) ||
  494. begins_with_ci(arg + 1, "IMPLIB:", &optlen) ||
  495. begins_with_ci(arg + 1, "INCLUDE:", &optlen) ||
  496. begins_with_ci(arg + 1, "INCREMENTAL", &optlen) ||
  497. begins_with_ci(arg + 1, "KEYCONTAINER:", &optlen) ||
  498. begins_with_ci(arg + 1, "KEYFILE:", &optlen) ||
  499. begins_with_ci(arg + 1, "LARGEADDRESSAWARE", &optlen) ||
  500. begins_with_ci(arg + 1, "LTCG", &optlen) ||
  501. begins_with_ci(arg + 1, "MACHINE:", &optlen) ||
  502. begins_with_ci(arg + 1, "MANIFEST", &optlen) ||
  503. begins_with_ci(arg + 1, "MANIFESTDEPENDENCY:", &optlen) ||
  504. begins_with_ci(arg + 1, "MANIFESTFILE:", &optlen) ||
  505. begins_with_ci(arg + 1, "MANIFESTUAC", &optlen) ||
  506. begins_with_ci(arg + 1, "MAPINFO:", &optlen) ||
  507. begins_with_ci(arg + 1, "MERGE:", &optlen) ||
  508. begins_with_ci(arg + 1, "NODEFAULTLIB", &optlen) ||
  509. begins_with_ci(arg + 1, "NXCOMPAT", &optlen) ||
  510. begins_with_ci(arg + 1, "OPT:", &optlen) ||
  511. begins_with_ci(arg + 1, "SAFESEH", &optlen) ||
  512. begins_with_ci(arg + 1, "SECTION:", &optlen) ||
  513. begins_with_ci(arg + 1, "STACK:", &optlen) ||
  514. begins_with_ci(arg + 1, "SUBSYSTEM:", &optlen) ||
  515. begins_with_ci(arg + 1, "SWAPRUN:", &optlen) ||
  516. begins_with_ci(arg + 1, "TLBID:", &optlen) ||
  517. begins_with_ci(arg + 1, "TSAWARE", &optlen) ||
  518. begins_with_ci(arg + 1, "VERBOSE", &optlen) ||
  519. begins_with_ci(arg + 1, "VERSION:", &optlen) ||
  520. begins_with_ci(arg + 1, "WX", &optlen)
  521. ) {
  522. add_arg(arg, len);
  523. continue;
  524. }
  525. // options with path arguments
  526. if (
  527. begins_with_ci(arg + 1, "LIBPATH:", &optlen) ||
  528. begins_with_ci(arg + 1, "MAP:", &optlen) ||
  529. begins_with_ci(arg + 1, "MIDL:", &optlen) ||
  530. begins_with_ci(arg + 1, "ORDER:", &optlen) ||
  531. begins_with_ci(arg + 1, "OUT:", &optlen) ||
  532. begins_with_ci(arg + 1, "PDB:", &optlen) ||
  533. begins_with_ci(arg + 1, "PDBSTRIPPED:", &optlen) ||
  534. begins_with_ci(arg + 1, "PGD:", &optlen) ||
  535. begins_with_ci(arg + 1, "STUB:", &optlen) ||
  536. begins_with_ci(arg + 1, "TLBOUT:", &optlen)
  537. ) {
  538. add_arg(arg, 1 + optlen);
  539. continue;
  540. }
  541. }
  542. // @response_file
  543. if (len >= 1 && arg[0] == '@') {
  544. add_arg(arg, 1);
  545. continue;
  546. }
  547. // TODO: check semantics of this
  548. // if a file of that name exists, assume it's a path
  549. if (len > 0 && access(arg, F_OK) == 0) {
  550. add_arg(arg, 0);
  551. continue;
  552. }
  553. // do not change
  554. add_arg(arg, len);
  555. continue;
  556. }
  557. #endif
  558. #if defined(WRAP_RC)
  559. while (i < argc) {
  560. // take argument
  561. char *arg = argv[i];
  562. int len = strlen(arg);
  563. i++;
  564. // parse as option?
  565. if (
  566. len >= 2 && (
  567. arg[0] == '-' ||
  568. (arg[0] == '/' && access(arg, F_OK) < 0)
  569. )
  570. ) {
  571. // read options
  572. int j = 1;
  573. while (j < len) {
  574. int optlen;
  575. // multi-letter options with no arguments
  576. if (
  577. j == 1 && (
  578. !strcasecmp(arg + j, "nologo") ||
  579. !strcasecmp(arg + j, "g1")
  580. )
  581. ) {
  582. add_arg(arg, len);
  583. goto next_arg;
  584. }
  585. // multi-letter options with path arguments
  586. if (
  587. j == 1 && (
  588. begins_with_ci(arg + j, "fm", &optlen) ||
  589. begins_with_ci(arg + j, "fo", &optlen)
  590. )
  591. ) {
  592. j += optlen;
  593. add_arg(arg, j);
  594. if (arg[j] == '\0' && i < argc) {
  595. add_arg(argv[i], 0);
  596. i++;
  597. }
  598. goto next_arg;
  599. }
  600. // single-letter options with no arguments
  601. if (
  602. begins_with_ci(arg + j, "?", &optlen) ||
  603. begins_with_ci(arg + j, "h", &optlen) ||
  604. begins_with_ci(arg + j, "n", &optlen) ||
  605. begins_with_ci(arg + j, "r", &optlen) ||
  606. begins_with_ci(arg + j, "v", &optlen) ||
  607. begins_with_ci(arg + j, "x", &optlen) ||
  608. begins_with_ci(arg + j, "y", &optlen) ||
  609. begins_with_ci(arg + j, "w", &optlen)
  610. ) {
  611. j += optlen;
  612. continue;
  613. }
  614. // single-letter options with literal arguments
  615. if (
  616. begins_with_ci(arg + j, "c", &optlen) ||
  617. begins_with_ci(arg + j, "d", &optlen) ||
  618. begins_with_ci(arg + j, "j", &optlen) ||
  619. begins_with_ci(arg + j, "k", &optlen) ||
  620. begins_with_ci(arg + j, "l", &optlen) ||
  621. begins_with_ci(arg + j, "u", &optlen) ||
  622. begins_with_ci(arg + j, "g", &optlen)
  623. ) {
  624. j += optlen;
  625. add_arg(arg, len);
  626. if (arg[j] == '\0' && i < argc) {
  627. add_arg(argv[i], strlen(argv[i]));
  628. i++;
  629. }
  630. goto next_arg;
  631. }
  632. // single-letter with path arguments
  633. if (
  634. begins_with_ci(arg + j, "I", &optlen) ||
  635. begins_with_ci(arg + j, "q", &optlen)
  636. ) {
  637. j += optlen;
  638. add_arg(arg, j);
  639. if (arg[j] == '\0' && i < argc) {
  640. add_arg(argv[i], 0);
  641. i++;
  642. }
  643. goto next_arg;
  644. }
  645. // unknown option, assume it's a path
  646. goto assume_path;
  647. }
  648. // options without arguments, add literally
  649. add_arg(arg, len);
  650. continue;
  651. }
  652. // assume its is a path
  653. assume_path:
  654. add_arg(arg, 0);
  655. continue;
  656. next_arg:;
  657. }
  658. #endif
  659. #if defined(WRAP_MT)
  660. while (i < argc) {
  661. // take argument
  662. char *arg = argv[i];
  663. int len = strlen(arg);
  664. i++;
  665. // parse as option?
  666. if (
  667. len >= 2 && (
  668. arg[0] == '-' ||
  669. (arg[0] == '/' && access(arg, F_OK) < 0)
  670. )
  671. ) {
  672. int optlen;
  673. // options with no value
  674. if (
  675. !strcasecmp(arg + 1, "manifest") ||
  676. !strcasecmp(arg + 1, "nologo") ||
  677. !strcasecmp(arg + 1, "notify_update") ||
  678. !strcasecmp(arg + 1, "hashupdate") ||
  679. !strcasecmp(arg + 1, "makecdfs") ||
  680. !strcasecmp(arg + 1, "validate_manifest") ||
  681. !strcasecmp(arg + 1, "canonicalize") ||
  682. !strcasecmp(arg + 1, "check_for_duplicates") ||
  683. !strcasecmp(arg + 1, "nologo")
  684. ) {
  685. add_arg(arg, len);
  686. continue;
  687. }
  688. // options with literal value
  689. if (
  690. begins_with_ci(arg + 1, "identity:", &optlen) ||
  691. begins_with_ci(arg + 1, "dll:", &optlen)
  692. ) {
  693. add_arg(arg, len);
  694. continue;
  695. }
  696. // options with path value
  697. if (
  698. begins_with_ci(arg + 1, "rgs:", &optlen) ||
  699. begins_with_ci(arg + 1, "tlb:", &optlen) ||
  700. begins_with_ci(arg + 1, "replacements:", &optlen) ||
  701. begins_with_ci(arg + 1, "managedassemblyname:", &optlen) ||
  702. begins_with_ci(arg + 1, "out:", &optlen) ||
  703. begins_with_ci(arg + 1, "hashupdate:", &optlen) ||
  704. begins_with_ci(arg + 1, "validate_file_hashes:", &optlen)
  705. ) {
  706. add_arg(arg, 1 + optlen);
  707. continue;
  708. }
  709. // options with path value and optional resource ID
  710. if (
  711. begins_with_ci(arg + 1, "inputresource:", &optlen) ||
  712. begins_with_ci(arg + 1, "outputresource:", &optlen) ||
  713. begins_with_ci(arg + 1, "updateresource:", &optlen)
  714. ) {
  715. int j;
  716. for (j = len - 1; j >= 1 + optlen; j--) {
  717. if (arg[j] == ';') {
  718. break;
  719. }
  720. }
  721. if (j >= 1 + optlen) {
  722. start_argument();
  723. write_argument_a(arg, 1 + optlen);
  724. write_argument_path(arg + (1 + optlen), j - (1 + optlen));
  725. write_argument_a(arg + j, len - j);
  726. end_argument();
  727. } else {
  728. add_arg(arg, 1 + optlen);
  729. }
  730. continue;
  731. }
  732. }
  733. // assume its is a path
  734. add_arg(arg, 0);
  735. continue;
  736. }
  737. #endif
  738. // zero terminate command line
  739. cmdline[cmdline_len] = '\0';
  740. print_a("Running: ", -1);
  741. print_w(cmdline, cmdline_len);
  742. print_a("\n", -1);
  743. // setup startup options
  744. STARTUPINFOW si;
  745. GetStartupInfoW(&si);
  746. si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
  747. si.wShowWindow = SW_HIDE;
  748. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  749. si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  750. si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
  751. // start process
  752. if (!CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
  753. fail("CreateProcessW failed");
  754. }
  755. Reactor_Init(&r);
  756. if (!Reactor_AddHandle(&r, pi.hProcess, process_handler, NULL)) {
  757. fail("Reactor_AddHandle failed");
  758. }
  759. // enter reactor
  760. Reactor_Exec(&r);
  761. return return_value;
  762. }