nmakehlp.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. /*
  2. * ----------------------------------------------------------------------------
  3. * nmakehlp.c --
  4. *
  5. * This is used to fix limitations within nmake and the environment.
  6. *
  7. * Copyright (c) 2002 by David Gravereaux.
  8. * Copyright (c) 2006 by Pat Thoyts
  9. *
  10. * See the file "license.terms" for information on usage and redistribution of
  11. * this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12. * ----------------------------------------------------------------------------
  13. */
  14. #define _CRT_SECURE_NO_DEPRECATE
  15. #include <windows.h>
  16. #define NO_SHLWAPI_GDI
  17. #define NO_SHLWAPI_STREAM
  18. #define NO_SHLWAPI_REG
  19. #include <shlwapi.h>
  20. #pragma comment (lib, "user32.lib")
  21. #pragma comment (lib, "kernel32.lib")
  22. #pragma comment (lib, "shlwapi.lib")
  23. #include <stdio.h>
  24. #include <math.h>
  25. /*
  26. * This library is required for x64 builds with _some_ versions of MSVC
  27. */
  28. #if defined(_M_IA64) || defined(_M_AMD64)
  29. #if _MSC_VER >= 1400 && _MSC_VER < 1500
  30. #pragma comment(lib, "bufferoverflowU")
  31. #endif
  32. #endif
  33. /* ISO hack for dumb VC++ */
  34. #ifdef _MSC_VER
  35. #define snprintf _snprintf
  36. #endif
  37. /* protos */
  38. static int CheckForCompilerFeature(const char *option);
  39. static int CheckForLinkerFeature(const char *option);
  40. static int IsIn(const char *string, const char *substring);
  41. static int SubstituteFile(const char *substs, const char *filename);
  42. static int QualifyPath(const char *path);
  43. static const char *GetVersionFromFile(const char *filename, const char *match);
  44. static DWORD WINAPI ReadFromPipe(LPVOID args);
  45. /* globals */
  46. #define CHUNK 25
  47. #define STATICBUFFERSIZE 1000
  48. typedef struct {
  49. HANDLE pipe;
  50. char buffer[STATICBUFFERSIZE];
  51. } pipeinfo;
  52. pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
  53. pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
  54. /*
  55. * exitcodes: 0 == no, 1 == yes, 2 == error
  56. */
  57. int
  58. main(
  59. int argc,
  60. char *argv[])
  61. {
  62. char msg[300];
  63. DWORD dwWritten;
  64. int chars;
  65. /*
  66. * Make sure children (cl.exe and link.exe) are kept quiet.
  67. */
  68. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
  69. /*
  70. * Make sure the compiler and linker aren't effected by the outside world.
  71. */
  72. SetEnvironmentVariable("CL", "");
  73. SetEnvironmentVariable("LINK", "");
  74. if (argc > 1 && *argv[1] == '-') {
  75. switch (*(argv[1]+1)) {
  76. case 'c':
  77. if (argc != 3) {
  78. chars = snprintf(msg, sizeof(msg) - 1,
  79. "usage: %s -c <compiler option>\n"
  80. "Tests for whether cl.exe supports an option\n"
  81. "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
  82. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
  83. &dwWritten, NULL);
  84. return 2;
  85. }
  86. return CheckForCompilerFeature(argv[2]);
  87. case 'l':
  88. if (argc != 3) {
  89. chars = snprintf(msg, sizeof(msg) - 1,
  90. "usage: %s -l <linker option>\n"
  91. "Tests for whether link.exe supports an option\n"
  92. "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
  93. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
  94. &dwWritten, NULL);
  95. return 2;
  96. }
  97. return CheckForLinkerFeature(argv[2]);
  98. case 'f':
  99. if (argc == 2) {
  100. chars = snprintf(msg, sizeof(msg) - 1,
  101. "usage: %s -f <string> <substring>\n"
  102. "Find a substring within another\n"
  103. "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
  104. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
  105. &dwWritten, NULL);
  106. return 2;
  107. } else if (argc == 3) {
  108. /*
  109. * If the string is blank, there is no match.
  110. */
  111. return 0;
  112. } else {
  113. return IsIn(argv[2], argv[3]);
  114. }
  115. case 's':
  116. if (argc == 2) {
  117. chars = snprintf(msg, sizeof(msg) - 1,
  118. "usage: %s -s <substitutions file> <file>\n"
  119. "Perform a set of string map type substutitions on a file\n"
  120. "exitcodes: 0\n",
  121. argv[0]);
  122. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
  123. &dwWritten, NULL);
  124. return 2;
  125. }
  126. return SubstituteFile(argv[2], argv[3]);
  127. case 'V':
  128. if (argc != 4) {
  129. chars = snprintf(msg, sizeof(msg) - 1,
  130. "usage: %s -V filename matchstring\n"
  131. "Extract a version from a file:\n"
  132. "eg: pkgIndex.tcl \"package ifneeded http\"",
  133. argv[0]);
  134. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
  135. &dwWritten, NULL);
  136. return 0;
  137. }
  138. printf("%s\n", GetVersionFromFile(argv[2], argv[3]));
  139. return 0;
  140. case 'Q':
  141. if (argc != 3) {
  142. chars = snprintf(msg, sizeof(msg) - 1,
  143. "usage: %s -Q path\n"
  144. "Emit the fully qualified path\n"
  145. "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
  146. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
  147. &dwWritten, NULL);
  148. return 2;
  149. }
  150. return QualifyPath(argv[2]);
  151. }
  152. }
  153. chars = snprintf(msg, sizeof(msg) - 1,
  154. "usage: %s -c|-f|-l|-Q|-s|-V ...\n"
  155. "This is a little helper app to equalize shell differences between WinNT and\n"
  156. "Win9x and get nmake.exe to accomplish its job.\n",
  157. argv[0]);
  158. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
  159. return 2;
  160. }
  161. static int
  162. CheckForCompilerFeature(
  163. const char *option)
  164. {
  165. STARTUPINFO si;
  166. PROCESS_INFORMATION pi;
  167. SECURITY_ATTRIBUTES sa;
  168. DWORD threadID;
  169. char msg[300];
  170. BOOL ok;
  171. HANDLE hProcess, h, pipeThreads[2];
  172. char cmdline[100];
  173. hProcess = GetCurrentProcess();
  174. ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
  175. ZeroMemory(&si, sizeof(STARTUPINFO));
  176. si.cb = sizeof(STARTUPINFO);
  177. si.dwFlags = STARTF_USESTDHANDLES;
  178. si.hStdInput = INVALID_HANDLE_VALUE;
  179. ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
  180. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  181. sa.lpSecurityDescriptor = NULL;
  182. sa.bInheritHandle = FALSE;
  183. /*
  184. * Create a non-inheritible pipe.
  185. */
  186. CreatePipe(&Out.pipe, &h, &sa, 0);
  187. /*
  188. * Dupe the write side, make it inheritible, and close the original.
  189. */
  190. DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
  191. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  192. /*
  193. * Same as above, but for the error side.
  194. */
  195. CreatePipe(&Err.pipe, &h, &sa, 0);
  196. DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
  197. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  198. /*
  199. * Base command line.
  200. */
  201. lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
  202. /*
  203. * Append our option for testing
  204. */
  205. lstrcat(cmdline, option);
  206. /*
  207. * Filename to compile, which exists, but is nothing and empty.
  208. */
  209. lstrcat(cmdline, " .\\nul");
  210. ok = CreateProcess(
  211. NULL, /* Module name. */
  212. cmdline, /* Command line. */
  213. NULL, /* Process handle not inheritable. */
  214. NULL, /* Thread handle not inheritable. */
  215. TRUE, /* yes, inherit handles. */
  216. DETACHED_PROCESS, /* No console for you. */
  217. NULL, /* Use parent's environment block. */
  218. NULL, /* Use parent's starting directory. */
  219. &si, /* Pointer to STARTUPINFO structure. */
  220. &pi); /* Pointer to PROCESS_INFORMATION structure. */
  221. if (!ok) {
  222. DWORD err = GetLastError();
  223. int chars = snprintf(msg, sizeof(msg) - 1,
  224. "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
  225. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
  226. FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
  227. (300-chars), 0);
  228. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
  229. return 2;
  230. }
  231. /*
  232. * Close our references to the write handles that have now been inherited.
  233. */
  234. CloseHandle(si.hStdOutput);
  235. CloseHandle(si.hStdError);
  236. WaitForInputIdle(pi.hProcess, 5000);
  237. CloseHandle(pi.hThread);
  238. /*
  239. * Start the pipe reader threads.
  240. */
  241. pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
  242. pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
  243. /*
  244. * Block waiting for the process to end.
  245. */
  246. WaitForSingleObject(pi.hProcess, INFINITE);
  247. CloseHandle(pi.hProcess);
  248. /*
  249. * Wait for our pipe to get done reading, should it be a little slow.
  250. */
  251. WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
  252. CloseHandle(pipeThreads[0]);
  253. CloseHandle(pipeThreads[1]);
  254. /*
  255. * Look for the commandline warning code in both streams.
  256. * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
  257. */
  258. return !(strstr(Out.buffer, "D4002") != NULL
  259. || strstr(Err.buffer, "D4002") != NULL
  260. || strstr(Out.buffer, "D9002") != NULL
  261. || strstr(Err.buffer, "D9002") != NULL
  262. || strstr(Out.buffer, "D2021") != NULL
  263. || strstr(Err.buffer, "D2021") != NULL);
  264. }
  265. static int
  266. CheckForLinkerFeature(
  267. const char *option)
  268. {
  269. STARTUPINFO si;
  270. PROCESS_INFORMATION pi;
  271. SECURITY_ATTRIBUTES sa;
  272. DWORD threadID;
  273. char msg[300];
  274. BOOL ok;
  275. HANDLE hProcess, h, pipeThreads[2];
  276. char cmdline[100];
  277. hProcess = GetCurrentProcess();
  278. ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
  279. ZeroMemory(&si, sizeof(STARTUPINFO));
  280. si.cb = sizeof(STARTUPINFO);
  281. si.dwFlags = STARTF_USESTDHANDLES;
  282. si.hStdInput = INVALID_HANDLE_VALUE;
  283. ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
  284. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  285. sa.lpSecurityDescriptor = NULL;
  286. sa.bInheritHandle = TRUE;
  287. /*
  288. * Create a non-inheritible pipe.
  289. */
  290. CreatePipe(&Out.pipe, &h, &sa, 0);
  291. /*
  292. * Dupe the write side, make it inheritible, and close the original.
  293. */
  294. DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
  295. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  296. /*
  297. * Same as above, but for the error side.
  298. */
  299. CreatePipe(&Err.pipe, &h, &sa, 0);
  300. DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
  301. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  302. /*
  303. * Base command line.
  304. */
  305. lstrcpy(cmdline, "link.exe -nologo ");
  306. /*
  307. * Append our option for testing.
  308. */
  309. lstrcat(cmdline, option);
  310. ok = CreateProcess(
  311. NULL, /* Module name. */
  312. cmdline, /* Command line. */
  313. NULL, /* Process handle not inheritable. */
  314. NULL, /* Thread handle not inheritable. */
  315. TRUE, /* yes, inherit handles. */
  316. DETACHED_PROCESS, /* No console for you. */
  317. NULL, /* Use parent's environment block. */
  318. NULL, /* Use parent's starting directory. */
  319. &si, /* Pointer to STARTUPINFO structure. */
  320. &pi); /* Pointer to PROCESS_INFORMATION structure. */
  321. if (!ok) {
  322. DWORD err = GetLastError();
  323. int chars = snprintf(msg, sizeof(msg) - 1,
  324. "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
  325. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
  326. FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
  327. (300-chars), 0);
  328. WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL);
  329. return 2;
  330. }
  331. /*
  332. * Close our references to the write handles that have now been inherited.
  333. */
  334. CloseHandle(si.hStdOutput);
  335. CloseHandle(si.hStdError);
  336. WaitForInputIdle(pi.hProcess, 5000);
  337. CloseHandle(pi.hThread);
  338. /*
  339. * Start the pipe reader threads.
  340. */
  341. pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
  342. pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
  343. /*
  344. * Block waiting for the process to end.
  345. */
  346. WaitForSingleObject(pi.hProcess, INFINITE);
  347. CloseHandle(pi.hProcess);
  348. /*
  349. * Wait for our pipe to get done reading, should it be a little slow.
  350. */
  351. WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
  352. CloseHandle(pipeThreads[0]);
  353. CloseHandle(pipeThreads[1]);
  354. /*
  355. * Look for the commandline warning code in the stderr stream.
  356. */
  357. return !(strstr(Out.buffer, "LNK1117") != NULL ||
  358. strstr(Err.buffer, "LNK1117") != NULL ||
  359. strstr(Out.buffer, "LNK4044") != NULL ||
  360. strstr(Err.buffer, "LNK4044") != NULL);
  361. }
  362. static DWORD WINAPI
  363. ReadFromPipe(
  364. LPVOID args)
  365. {
  366. pipeinfo *pi = (pipeinfo *) args;
  367. char *lastBuf = pi->buffer;
  368. DWORD dwRead;
  369. BOOL ok;
  370. again:
  371. if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
  372. CloseHandle(pi->pipe);
  373. return (DWORD)-1;
  374. }
  375. ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
  376. if (!ok || dwRead == 0) {
  377. CloseHandle(pi->pipe);
  378. return 0;
  379. }
  380. lastBuf += dwRead;
  381. goto again;
  382. return 0; /* makes the compiler happy */
  383. }
  384. static int
  385. IsIn(
  386. const char *string,
  387. const char *substring)
  388. {
  389. return (strstr(string, substring) != NULL);
  390. }
  391. /*
  392. * GetVersionFromFile --
  393. * Looks for a match string in a file and then returns the version
  394. * following the match where a version is anything acceptable to
  395. * package provide or package ifneeded.
  396. */
  397. static const char *
  398. GetVersionFromFile(
  399. const char *filename,
  400. const char *match)
  401. {
  402. size_t cbBuffer = 100;
  403. static char szBuffer[100];
  404. char *szResult = NULL;
  405. FILE *fp = fopen(filename, "rt");
  406. if (fp != NULL) {
  407. /*
  408. * Read data until we see our match string.
  409. */
  410. while (fgets(szBuffer, cbBuffer, fp) != NULL) {
  411. LPSTR p, q;
  412. p = strstr(szBuffer, match);
  413. if (p != NULL) {
  414. /*
  415. * Skip to first digit.
  416. */
  417. while (*p && !isdigit(*p)) {
  418. ++p;
  419. }
  420. /*
  421. * Find ending whitespace.
  422. */
  423. q = p;
  424. while (*q && (isalnum(*q) || *q == '.')) {
  425. ++q;
  426. }
  427. memcpy(szBuffer, p, q - p);
  428. szBuffer[q-p] = 0;
  429. szResult = szBuffer;
  430. break;
  431. }
  432. }
  433. fclose(fp);
  434. }
  435. return szResult;
  436. }
  437. /*
  438. * List helpers for the SubstituteFile function
  439. */
  440. typedef struct list_item_t {
  441. struct list_item_t *nextPtr;
  442. char * key;
  443. char * value;
  444. } list_item_t;
  445. /* insert a list item into the list (list may be null) */
  446. static list_item_t *
  447. list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
  448. {
  449. list_item_t *itemPtr = malloc(sizeof(list_item_t));
  450. if (itemPtr) {
  451. itemPtr->key = strdup(key);
  452. itemPtr->value = strdup(value);
  453. itemPtr->nextPtr = NULL;
  454. while(*listPtrPtr) {
  455. listPtrPtr = &(*listPtrPtr)->nextPtr;
  456. }
  457. *listPtrPtr = itemPtr;
  458. }
  459. return itemPtr;
  460. }
  461. static void
  462. list_free(list_item_t **listPtrPtr)
  463. {
  464. list_item_t *tmpPtr, *listPtr = *listPtrPtr;
  465. while (listPtr) {
  466. tmpPtr = listPtr;
  467. listPtr = listPtr->nextPtr;
  468. free(tmpPtr->key);
  469. free(tmpPtr->value);
  470. free(tmpPtr);
  471. }
  472. }
  473. /*
  474. * SubstituteFile --
  475. * As windows doesn't provide anything useful like sed and it's unreliable
  476. * to use the tclsh you are building against (consider x-platform builds -
  477. * eg compiling AMD64 target from IX86) we provide a simple substitution
  478. * option here to handle autoconf style substitutions.
  479. * The substitution file is whitespace and line delimited. The file should
  480. * consist of lines matching the regular expression:
  481. * \s*\S+\s+\S*$
  482. *
  483. * Usage is something like:
  484. * nmakehlp -S << $** > $@
  485. * @PACKAGE_NAME@ $(PACKAGE_NAME)
  486. * @PACKAGE_VERSION@ $(PACKAGE_VERSION)
  487. * <<
  488. */
  489. static int
  490. SubstituteFile(
  491. const char *substitutions,
  492. const char *filename)
  493. {
  494. size_t cbBuffer = 1024;
  495. static char szBuffer[1024], szCopy[1024];
  496. char *szResult = NULL;
  497. list_item_t *substPtr = NULL;
  498. FILE *fp, *sp;
  499. fp = fopen(filename, "rt");
  500. if (fp != NULL) {
  501. /*
  502. * Build a list of substutitions from the first filename
  503. */
  504. sp = fopen(substitutions, "rt");
  505. if (sp != NULL) {
  506. while (fgets(szBuffer, cbBuffer, sp) != NULL) {
  507. char *ks, *ke, *vs, *ve;
  508. ks = szBuffer;
  509. while (ks && *ks && isspace(*ks)) ++ks;
  510. ke = ks;
  511. while (ke && *ke && !isspace(*ke)) ++ke;
  512. vs = ke;
  513. while (vs && *vs && isspace(*vs)) ++vs;
  514. ve = vs;
  515. while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
  516. *ke = 0, *ve = 0;
  517. list_insert(&substPtr, ks, vs);
  518. }
  519. fclose(sp);
  520. }
  521. /* debug: dump the list */
  522. #ifdef _DEBUG
  523. {
  524. int n = 0;
  525. list_item_t *p = NULL;
  526. for (p = substPtr; p != NULL; p = p->nextPtr, ++n) {
  527. fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value);
  528. }
  529. }
  530. #endif
  531. /*
  532. * Run the substitutions over each line of the input
  533. */
  534. while (fgets(szBuffer, cbBuffer, fp) != NULL) {
  535. list_item_t *p = NULL;
  536. for (p = substPtr; p != NULL; p = p->nextPtr) {
  537. char *m = strstr(szBuffer, p->key);
  538. if (m) {
  539. char *cp, *op, *sp;
  540. cp = szCopy;
  541. op = szBuffer;
  542. while (op != m) *cp++ = *op++;
  543. sp = p->value;
  544. while (sp && *sp) *cp++ = *sp++;
  545. op += strlen(p->key);
  546. while (*op) *cp++ = *op++;
  547. *cp = 0;
  548. memcpy(szBuffer, szCopy, sizeof(szCopy));
  549. }
  550. }
  551. printf(szBuffer);
  552. }
  553. list_free(&substPtr);
  554. }
  555. fclose(fp);
  556. return 0;
  557. }
  558. /*
  559. * QualifyPath --
  560. *
  561. * This composes the current working directory with a provided path
  562. * and returns the fully qualified and normalized path.
  563. * Mostly needed to setup paths for testing.
  564. */
  565. static int
  566. QualifyPath(
  567. const char *szPath)
  568. {
  569. char szCwd[MAX_PATH + 1];
  570. char szTmp[MAX_PATH + 1];
  571. char *p;
  572. GetCurrentDirectory(MAX_PATH, szCwd);
  573. while ((p = strchr(szPath, '/')) && *p)
  574. *p = '\\';
  575. PathCombine(szTmp, szCwd, szPath);
  576. PathCanonicalize(szCwd, szTmp);
  577. printf("%s\n", szCwd);
  578. return 0;
  579. }
  580. /*
  581. * Local variables:
  582. * mode: c
  583. * c-basic-offset: 4
  584. * fill-column: 78
  585. * indent-tabs-mode: t
  586. * tab-width: 8
  587. * End:
  588. */