nmakehlp.c 21 KB

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