"Fossies" - the Fresh Open Source Software Archive

Member "sqlite-autoconf-3320300/tea/win/nmakehlp.c" (18 Jun 2020, 17368 Bytes) of package /linux/misc/sqlite-autoconf-3320300.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "nmakehlp.c" see the Fossies "Dox" file reference documentation.

    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 file\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 WinNT 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         unsigned char *ks, *ke, *vs, *ve;
  607         ks = (unsigned char*)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, (char*)ks, (char*)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  */