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