nmakehlp.c 21 KB

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