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