Home | History | Annotate | Download | only in MakeDeps
      1 /*++
      2 
      3 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
      4 This program and the accompanying materials
      5 are licensed and made available under the terms and conditions of the BSD License
      6 which accompanies this distribution.  The full text of the license may be found at
      7 http://opensource.org/licenses/bsd-license.php
      8 
      9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     11 
     12 Module Name:
     13 
     14   MakeDeps.c
     15 
     16 Abstract:
     17 
     18   Recursively scan source files to find include files and emit them to
     19   create dependency lists.
     20 
     21 --*/
     22 
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include <stdlib.h>
     26 #include <ctype.h>
     27 
     28 #include "Tiano.h"
     29 #include "EfiUtilityMsgs.h"
     30 
     31 //
     32 // Structure to maintain a linked list of strings
     33 //
     34 typedef struct _STRING_LIST {
     35   struct _STRING_LIST *Next;
     36   char                *Str;
     37 } STRING_LIST;
     38 
     39 #define UTILITY_NAME      "MakeDeps"
     40 #define UTILITY_VERSION   "v1.0"
     41 
     42 #define MAX_LINE_LEN      2048
     43 #define MAX_PATH          2048
     44 #define START_NEST_DEPTH  1
     45 #define MAX_NEST_DEPTH    1000  // just in case we get in an endless loop.
     46 //
     47 // Define the relative paths used by the special #include macros
     48 //
     49 #define PROTOCOL_DIR_PATH       "Protocol\\"
     50 #define GUID_DIR_PATH           "Guid\\"
     51 #define ARCH_PROTOCOL_DIR_PATH  "ArchProtocol\\"
     52 #define PPI_PROTOCOL_DIR_PATH   "Ppi\\"
     53 
     54 //
     55 // Use this structure to keep track of all the special #include forms
     56 //
     57 typedef struct {
     58   INT8  *IncludeMacroName;
     59   INT8  *PathName;
     60 } INCLUDE_MACRO_CONVERSION;
     61 
     62 //
     63 // This data is used to convert #include macros like:
     64 //    #include EFI_PROTOCOL_DEFINITION(xxx)
     65 // into
     66 //    #include Protocol/xxx/xxx.h
     67 //
     68 static const INCLUDE_MACRO_CONVERSION mMacroConversion[] = {
     69   "EFI_PROTOCOL_DEFINITION",
     70   PROTOCOL_DIR_PATH,
     71   "EFI_GUID_DEFINITION",
     72   GUID_DIR_PATH,
     73   "EFI_ARCH_PROTOCOL_DEFINITION",
     74   ARCH_PROTOCOL_DIR_PATH,
     75   "EFI_PROTOCOL_PRODUCER",
     76   PROTOCOL_DIR_PATH,
     77   "EFI_PROTOCOL_CONSUMER",
     78   PROTOCOL_DIR_PATH,
     79   "EFI_PROTOCOL_DEPENDENCY",
     80   PROTOCOL_DIR_PATH,
     81   "EFI_ARCH_PROTOCOL_PRODUCER",
     82   ARCH_PROTOCOL_DIR_PATH,
     83   "EFI_ARCH_PROTOCOL_CONSUMER",
     84   ARCH_PROTOCOL_DIR_PATH,
     85   "EFI_ARCH_PROTOCOL_DEPENDENCY",
     86   ARCH_PROTOCOL_DIR_PATH,
     87   "EFI_PPI_DEFINITION",
     88   PPI_PROTOCOL_DIR_PATH,
     89   "EFI_PPI_PRODUCER",
     90   PPI_PROTOCOL_DIR_PATH,
     91   "EFI_PPI_CONSUMER",
     92   PPI_PROTOCOL_DIR_PATH,
     93   "EFI_PPI_DEPENDENCY",
     94   PPI_PROTOCOL_DIR_PATH,
     95   NULL,
     96   NULL
     97 };
     98 
     99 typedef struct _SYMBOL {
    100   struct _SYMBOL  *Next;
    101   INT8            *Name;
    102   INT8            *Value;
    103 } SYMBOL;
    104 
    105 typedef enum {
    106   SearchCurrentDir,
    107   SearchIncludePaths,
    108   SearchAllPaths,
    109 } FILE_SEARCH_TYPE;
    110 
    111 //
    112 // Here's all our globals. We need a linked list of include paths, a linked
    113 // list of source files, a linked list of subdirectories (appended to each
    114 // include path when searching), and flags to keep track of command-line options.
    115 //
    116 static struct {
    117   STRING_LIST *IncludePaths;            // all include paths to search
    118   STRING_LIST *ParentPaths;             // all parent paths to search
    119   STRING_LIST *SourceFiles;             // all source files to parse
    120   STRING_LIST *SubDirs;                 // appended to each include path when searching
    121   SYMBOL      *SymbolTable;             // for replacement strings
    122   FILE        *OutFptr;                 // output dependencies to this file
    123   BOOLEAN     Verbose;                  // for more detailed output
    124   BOOLEAN     IgnoreNotFound;           // no warnings if files not found
    125   BOOLEAN     QuietMode;                // -q - don't print missing file warnings
    126   BOOLEAN     NoSystem;                 // don't process #include <system> files
    127   BOOLEAN     NeverFail;                // always return success
    128   BOOLEAN     NoDupes;                  // to not list duplicate dependency files (for timing purposes)
    129   BOOLEAN     UseSumDeps;               // use summary dependency files if found
    130   BOOLEAN     IsAsm;                    // The SourceFiles are assembler files
    131   BOOLEAN     IsCl;                     // The SourceFiles are the output of cl with /showIncludes
    132   INT8        TargetFileName[MAX_PATH]; // target object filename
    133   INT8        SumDepsPath[MAX_PATH];    // path to summary files
    134   INT8        TmpFileName[MAX_PATH];    // temp file name for output file
    135   INT8        *OutFileName;             // -o option
    136 } mGlobals;
    137 
    138 static
    139 STATUS
    140 ProcessFile (
    141   INT8              *TargetFileName,
    142   INT8              *FileName,
    143   UINT32            NestDepth,
    144   STRING_LIST       *ProcessedFiles,
    145   FILE_SEARCH_TYPE  FileSearchType
    146   );
    147 
    148 static
    149 STATUS
    150 ProcessClOutput (
    151   INT8            *TargetFileName,
    152   INT8            *FileName,
    153   STRING_LIST     *ProcessedFiles
    154   );
    155 
    156 static
    157 FILE  *
    158 FindFile (
    159   INT8              *FileName,
    160   UINT32            FileNameLen,
    161   FILE_SEARCH_TYPE  FileSearchType
    162   );
    163 
    164 static
    165 void
    166 PrintDependency (
    167   INT8    *Target,
    168   INT8    *DependentFile
    169   );
    170 
    171 static
    172 void
    173 ReplaceSymbols (
    174   INT8    *Str,
    175   UINT32  StrSize
    176   );
    177 
    178 static
    179 STATUS
    180 ProcessArgs (
    181   int   Argc,
    182   char  *Argv[]
    183   );
    184 
    185 static
    186 void
    187 Usage (
    188   VOID
    189   );
    190 
    191 static
    192 void
    193 FreeLists (
    194   VOID
    195   );
    196 
    197 int
    198 main (
    199   int   Argc,
    200   char  *Argv[]
    201   )
    202 /*++
    203 
    204 Routine Description:
    205 
    206   Call the routine to parse the command-line options, then process each file
    207   to build dependencies.
    208 
    209 Arguments:
    210 
    211   Argc - Standard C main() argc.
    212   Argv - Standard C main() argv.
    213 
    214 Returns:
    215 
    216   0       if successful
    217   nonzero otherwise
    218 
    219 --*/
    220 {
    221   STRING_LIST *File;
    222   STRING_LIST ProcessedFiles;
    223   STRING_LIST *TempList;
    224   STATUS      Status;
    225   INT8        *Cptr;
    226   INT8        TargetFileName[MAX_PATH];
    227 
    228   SetUtilityName (UTILITY_NAME);
    229   //
    230   // Process the command-line arguments
    231   //
    232   Status = ProcessArgs (Argc, Argv);
    233   if (Status != STATUS_SUCCESS) {
    234     return STATUS_ERROR;
    235   }
    236   //
    237   // Go through the list of source files and process each.
    238   //
    239   memset (&ProcessedFiles, 0, sizeof (STRING_LIST));
    240   File = mGlobals.SourceFiles;
    241   while (File != NULL) {
    242     //
    243     // Clear out our list of processed files
    244     //
    245     TempList = ProcessedFiles.Next;
    246     while (ProcessedFiles.Next != NULL) {
    247       TempList = ProcessedFiles.Next->Next;
    248       free (ProcessedFiles.Next->Str);
    249       free (ProcessedFiles.Next);
    250       ProcessedFiles.Next = TempList;
    251     }
    252     //
    253     // Replace filename extension with ".obj" if they did not
    254     // specifically specify the target file
    255     //
    256     if (mGlobals.TargetFileName[0] == 0) {
    257       strcpy (TargetFileName, File->Str);
    258       //
    259       // Find the .extension
    260       //
    261       for (Cptr = TargetFileName + strlen (TargetFileName) - 1;
    262            (*Cptr != '\\') && (Cptr > TargetFileName) && (*Cptr != '.');
    263            Cptr--
    264           )
    265         ;
    266       if (Cptr == TargetFileName) {
    267         Error (NULL, 0, 0, File->Str, "could not locate extension in filename");
    268         goto Finish;
    269       }
    270       //
    271       // Tack on the ".obj"
    272       //
    273       strcpy (Cptr, ".obj");
    274     } else {
    275       //
    276       // Copy the target filename they specified
    277       //
    278       strcpy (TargetFileName, mGlobals.TargetFileName);
    279     }
    280 
    281     if (mGlobals.IsCl) {
    282       Status = ProcessClOutput (TargetFileName, File->Str, &ProcessedFiles);
    283     } else {
    284       Status = ProcessFile (TargetFileName, File->Str, START_NEST_DEPTH,
    285                             &ProcessedFiles, SearchCurrentDir);
    286     }
    287     if (Status != STATUS_SUCCESS) {
    288       goto Finish;
    289     }
    290 
    291     File = File->Next;
    292   }
    293 
    294 Finish:
    295   //
    296   // Free up memory
    297   //
    298   FreeLists ();
    299   //
    300   // Free up our processed files list
    301   //
    302   TempList = ProcessedFiles.Next;
    303   while (ProcessedFiles.Next != NULL) {
    304     TempList = ProcessedFiles.Next->Next;
    305     free (ProcessedFiles.Next->Str);
    306     free (ProcessedFiles.Next);
    307     ProcessedFiles.Next = TempList;
    308   }
    309   //
    310   // Close our temp output file
    311   //
    312   if ((mGlobals.OutFptr != stdout) && (mGlobals.OutFptr != NULL)) {
    313     fclose (mGlobals.OutFptr);
    314   }
    315 
    316   if (mGlobals.NeverFail) {
    317     return STATUS_SUCCESS;
    318   }
    319 
    320   if (mGlobals.OutFileName != NULL) {
    321     if (GetUtilityStatus () == STATUS_ERROR) {
    322       //
    323       // If any errors, then delete our temp output
    324       // Also try to delete target file to improve the incremental build
    325       //
    326       remove (mGlobals.TmpFileName);
    327       remove (TargetFileName);
    328     } else {
    329       //
    330       // Otherwise, rename temp file to output file
    331       //
    332       remove (mGlobals.OutFileName);
    333       rename (mGlobals.TmpFileName, mGlobals.OutFileName);
    334     }
    335   }
    336 
    337   return GetUtilityStatus ();
    338 }
    339 
    340 static
    341 STATUS
    342 ProcessFile (
    343   INT8              *TargetFileName,
    344   INT8              *FileName,
    345   UINT32            NestDepth,
    346   STRING_LIST       *ProcessedFiles,
    347   FILE_SEARCH_TYPE  FileSearchType
    348   )
    349 /*++
    350 
    351 Routine Description:
    352 
    353   Given a source file name, open the file and parse all #include lines.
    354 
    355 Arguments:
    356 
    357   TargetFileName - name of the usually .obj target
    358   FileName       - name of the file to process
    359   NestDepth      - how deep we're nested in includes
    360   ProcessedFiles - list of processed files.
    361   FileSearchType - search type for FileName
    362 
    363 Returns:
    364 
    365   standard status.
    366 
    367 --*/
    368 {
    369   FILE        *Fptr;
    370   INT8        Line[MAX_LINE_LEN];
    371   INT8        *Cptr;
    372   INT8        *EndPtr;
    373   INT8        *SaveCptr;
    374   INT8        EndChar;
    375   INT8        FileNameCopy[MAX_PATH];
    376   INT8        MacroIncludeFileName[MAX_LINE_LEN];
    377   INT8        SumDepsFile[MAX_PATH];
    378   STATUS      Status;
    379   UINT32      Index;
    380   UINT32      LineNum;
    381   STRING_LIST *ListPtr;
    382   STRING_LIST ParentPath;
    383 
    384   Status  = STATUS_SUCCESS;
    385   Fptr    = NULL;
    386   //
    387   // Print the file being processed. Indent so you can tell the include nesting
    388   // depth.
    389   //
    390   if (mGlobals.Verbose) {
    391     fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', FileName);
    392   }
    393   //
    394   // If we're using summary dependency files, and a matching .dep file is
    395   // found for this file, then just emit the summary dependency file as
    396   // a dependency and return.
    397   //
    398   if (mGlobals.UseSumDeps) {
    399     strcpy (SumDepsFile, mGlobals.SumDepsPath);
    400     strcat (SumDepsFile, FileName);
    401     for (Cptr = SumDepsFile + strlen (SumDepsFile) - 1;
    402          (*Cptr != '\\') && (Cptr > SumDepsFile) && (*Cptr != '.');
    403          Cptr--
    404         )
    405       ;
    406     if (*Cptr == '.') {
    407       strcpy (Cptr, ".dep");
    408     } else {
    409       strcat (SumDepsFile, ".dep");
    410     }
    411     //
    412     // See if the summary dep file exists. Could use _stat() function, but
    413     // it's less portable.
    414     //
    415     if ((Fptr = fopen (SumDepsFile, "r")) != NULL) {
    416       PrintDependency (TargetFileName, SumDepsFile);
    417       fclose (Fptr);
    418       return STATUS_SUCCESS;
    419     }
    420   }
    421 
    422   //
    423   // Make sure we didn't exceed our maximum nesting depth
    424   //
    425   if (NestDepth > MAX_NEST_DEPTH) {
    426     Error (NULL, 0, 0, FileName, "max nesting depth exceeded on file");
    427     goto Finish;
    428   }
    429   //
    430   // Make a local copy of the filename. Then we can manipulate it
    431   // if we have to.
    432   //
    433   strcpy (FileNameCopy, FileName);
    434 
    435   if (FileSearchType == SearchCurrentDir) {
    436     //
    437     // Try to open the source file locally
    438     //
    439     if ((Fptr = fopen (FileNameCopy, "r")) == NULL) {
    440       Error (NULL, 0, 0, FileNameCopy, "could not open source file");
    441       return STATUS_ERROR;
    442     }
    443   } else {
    444     //
    445     // Try to find it among the paths.
    446     //
    447     Fptr = FindFile (FileNameCopy, sizeof (FileNameCopy), FileSearchType);
    448     if (Fptr == NULL) {
    449       //
    450       // If this is not the top-level file, and the command-line argument
    451       // said to ignore missing files, then return ok
    452       //
    453       if (NestDepth != START_NEST_DEPTH) {
    454         if (mGlobals.IgnoreNotFound) {
    455           if (!mGlobals.QuietMode) {
    456             DebugMsg (NULL, 0, 0, FileNameCopy, "could not find file");
    457           }
    458 
    459           return STATUS_SUCCESS;
    460         } else {
    461           Error (NULL, 0, 0, FileNameCopy, "could not find file");
    462           return STATUS_ERROR;
    463         }
    464       } else {
    465         //
    466         // Top-level (first) file. Emit an error.
    467         //
    468         Error (NULL, 0, 0, FileNameCopy, "could not find file");
    469         return STATUS_ERROR;
    470       }
    471     }
    472   }
    473 
    474   //
    475   // If we're not doing duplicates, and we've already seen this filename,
    476   // then return
    477   //
    478   if (mGlobals.NoDupes) {
    479     for (ListPtr = ProcessedFiles->Next; ListPtr != NULL; ListPtr = ListPtr->Next) {
    480       if (_stricmp (FileNameCopy, ListPtr->Str) == 0) {
    481         break;
    482       }
    483     }
    484     //
    485     // If we found a match, we're done. If we didn't, create a new element
    486     // and add it to the list.
    487     //
    488     if (ListPtr != NULL) {
    489       //
    490       // Print a message if verbose mode
    491       //
    492       if (mGlobals.Verbose) {
    493         DebugMsg (NULL, 0, 0, FileNameCopy, "duplicate include -- not processed again");
    494       }
    495       fclose (Fptr);
    496       return STATUS_SUCCESS;
    497     }
    498 
    499     ListPtr       = malloc (sizeof (STRING_LIST));
    500     ListPtr->Str  = malloc (strlen (FileNameCopy) + 1);
    501     strcpy (ListPtr->Str, FileNameCopy);
    502     ListPtr->Next         = ProcessedFiles->Next;
    503     ProcessedFiles->Next  = ListPtr;
    504   }
    505 
    506   //
    507   // Print the dependency, with string substitution
    508   //
    509   PrintDependency (TargetFileName, FileNameCopy);
    510 
    511   //
    512   // Get the file path and push to ParentPaths
    513   //
    514   Cptr = FileNameCopy + strlen (FileNameCopy) - 1;
    515   for (; (Cptr > FileNameCopy) && (*Cptr != '\\') && (*Cptr != '/'); Cptr--);
    516   if ((*Cptr == '\\') || (*Cptr == '/')) {
    517     *(Cptr + 1) = 0;
    518   } else {
    519     strcpy (FileNameCopy, ".\\");
    520   }
    521   ParentPath.Next = mGlobals.ParentPaths;
    522   ParentPath.Str = FileNameCopy;
    523   mGlobals.ParentPaths = &ParentPath;
    524 
    525   //
    526   // Now read in lines and find all #include lines. Allow them to indent, and
    527   // to put spaces between the # and include.
    528   //
    529   LineNum = 0;
    530   while ((fgets (Line, sizeof (Line), Fptr) != NULL) && (Status == STATUS_SUCCESS)) {
    531     LineNum++;
    532     Cptr = Line;
    533     //
    534     // Skip preceeding spaces on the line
    535     //
    536     while (*Cptr && (isspace (*Cptr))) {
    537       Cptr++;
    538     }
    539     //
    540     // Check for # character, there is no # for asm
    541     //
    542     if ((*Cptr == '#') || (mGlobals.IsAsm)) {
    543       if (*Cptr == '#') {
    544         Cptr++;
    545       }
    546 
    547       //
    548       // Check for "include", case insensitive for asm
    549       //
    550       while (*Cptr && (isspace (*Cptr))) {
    551         Cptr++;
    552       }
    553       if (((!mGlobals.IsAsm) && (strncmp (Cptr, "include", 7) == 0)) ||
    554           (mGlobals.IsAsm && (_strnicmp (Cptr, "include", 7) == 0))) {
    555         //
    556         // Skip over "include" and move on to filename as "file" or <file> or file for asm
    557         //
    558         Cptr += 7;
    559         while (*Cptr && (isspace (*Cptr))) {
    560           Cptr++;
    561         }
    562 
    563         if (*Cptr == '<') {
    564           EndChar = '>';
    565         } else if (*Cptr == '"') {
    566           EndChar = '"';
    567         } else if (mGlobals.IsAsm) {
    568           //
    569           // Handle include file for asm
    570           // Set EndChar to null so we fall through on processing below.
    571           //
    572           EndChar = 0;
    573 
    574           //
    575           // Look for the end of include file name
    576           //
    577           EndPtr = Cptr;
    578           while (*EndPtr && (!isspace (*EndPtr))) {
    579             EndPtr++;
    580           }
    581 
    582           //
    583           // Null terminate the filename and try to process it.
    584           //
    585           *EndPtr = 0;
    586           Status  = ProcessFile (TargetFileName, Cptr, NestDepth + 1,
    587                                  ProcessedFiles, SearchAllPaths);
    588         } else {
    589           //
    590           // Handle special #include MACRO_NAME(file)
    591           // Set EndChar to null so we fall through on processing below.
    592           //
    593           EndChar = 0;
    594           //
    595           // Look for all the special include macros and convert accordingly.
    596           //
    597           for (Index = 0; mMacroConversion[Index].IncludeMacroName != NULL; Index++) {
    598             //
    599             // Save the start of the string in case some macros are substrings
    600             // of others.
    601             //
    602             SaveCptr = Cptr;
    603             if (strncmp (
    604                   Cptr,
    605                   mMacroConversion[Index].IncludeMacroName,
    606                   strlen (mMacroConversion[Index].IncludeMacroName)
    607                   ) == 0) {
    608               //
    609               // Skip over the macro name
    610               //
    611               Cptr += strlen (mMacroConversion[Index].IncludeMacroName);
    612               //
    613               // Skip over open parenthesis, blank spaces, then find closing
    614               // parenthesis or blank space
    615               //
    616               while (*Cptr && (isspace (*Cptr))) {
    617                 Cptr++;
    618               }
    619 
    620               if (*Cptr == '(') {
    621                 Cptr++;
    622                 while (*Cptr && (isspace (*Cptr))) {
    623                   Cptr++;
    624                 }
    625 
    626                 EndPtr = Cptr;
    627                 while (*EndPtr && !isspace (*EndPtr) && (*EndPtr != ')')) {
    628                   EndPtr++;
    629                 }
    630 
    631                 *EndPtr = 0;
    632                 //
    633                 // Create the path
    634                 //
    635                 strcpy (MacroIncludeFileName, mMacroConversion[Index].PathName);
    636                 strcat (MacroIncludeFileName, Cptr);
    637                 strcat (MacroIncludeFileName, "\\");
    638                 strcat (MacroIncludeFileName, Cptr);
    639                 strcat (MacroIncludeFileName, ".h");
    640                 //
    641                 // Process immediately, then break out of the outside FOR loop.
    642                 //
    643                 Status = ProcessFile (TargetFileName, MacroIncludeFileName, NestDepth + 1,
    644                                       ProcessedFiles, SearchAllPaths);
    645                 break;
    646               }
    647             }
    648             //
    649             // Restore the start
    650             //
    651             Cptr = SaveCptr;
    652           }
    653           //
    654           // Don't recognize the include line? Ignore it. We assume that the
    655           // file compiles anyway.
    656           //
    657           if (mMacroConversion[Index].IncludeMacroName == NULL) {
    658             //
    659             // Warning (FileNameCopy, LineNum, 0, "could not parse line", NULL);
    660             // Status = STATUS_WARNING;
    661             //
    662           }
    663         }
    664         //
    665         // Process "normal" includes. If the endchar is 0, then the
    666         // file has already been processed. Otherwise look for the
    667         // endchar > or ", and process the include file.
    668         //
    669         if (EndChar != 0) {
    670           Cptr++;
    671           EndPtr = Cptr;
    672           while (*EndPtr && (*EndPtr != EndChar)) {
    673             EndPtr++;
    674           }
    675 
    676           if (*EndPtr == EndChar) {
    677             //
    678             // If we're processing it, do it
    679             //
    680             if (EndChar != '>') {
    681               //
    682               // Null terminate the filename and try to process it.
    683               //
    684               *EndPtr = 0;
    685               Status  = ProcessFile (TargetFileName, Cptr, NestDepth + 1,
    686                                      ProcessedFiles, SearchAllPaths);
    687             } else if (!mGlobals.NoSystem) {
    688               //
    689               // Null terminate the filename and try to process it.
    690               //
    691               *EndPtr = 0;
    692               Status  = ProcessFile (TargetFileName, Cptr, NestDepth + 1,
    693                                      ProcessedFiles, SearchIncludePaths);
    694             }
    695           } else {
    696             Warning (FileNameCopy, LineNum, 0, "malformed include", "missing closing %c", EndChar);
    697             Status = STATUS_WARNING;
    698             goto Finish;
    699           }
    700         }
    701       }
    702     }
    703   }
    704   //
    705   // Pop the file path from ParentPaths
    706   //
    707   mGlobals.ParentPaths = ParentPath.Next;
    708 
    709 Finish:
    710   //
    711   // Close open files and return status
    712   //
    713   if (Fptr != NULL) {
    714     fclose (Fptr);
    715   }
    716 
    717   return Status;
    718 }
    719 
    720 static
    721 STATUS
    722 ProcessClOutput (
    723   INT8            *TargetFileName,
    724   INT8            *FileName,
    725   STRING_LIST     *ProcessedFiles
    726   )
    727 /*++
    728 
    729 Routine Description:
    730 
    731   Given a source file name, open the file and parse all "Note: including file: xxx.h" lines.
    732 
    733 Arguments:
    734 
    735   TargetFileName - name of the usually .obj target
    736   FileName       - name of the file to process
    737   ProcessedFiles - list of processed files.
    738 
    739 Returns:
    740 
    741   standard status.
    742 
    743 --*/
    744 {
    745   FILE        *Fptr;
    746   INT8        Line[MAX_LINE_LEN];
    747   INT8        IncludeFileName[MAX_LINE_LEN];
    748   STRING_LIST *ListPtr;
    749   BOOLEAN     ClError;
    750   INT32       Ret;
    751   INT8        Char;
    752 
    753   if ((Fptr = fopen (FileName, "r")) == NULL) {
    754     Error (NULL, 0, 0, FileName, "could not open file for reading");
    755     return STATUS_ERROR;
    756   }
    757   if (fgets (Line, sizeof (Line), Fptr) != NULL) {
    758     //
    759     // First line is the source file name, print it
    760     //
    761     printf ("%s", Line);
    762   } else {
    763     //
    764     // No output from cl
    765     //
    766     fclose (Fptr);
    767     Error (NULL, 0, 0, NULL, "incorrect cl tool path may be used ");
    768     return STATUS_ERROR;
    769   }
    770 
    771   ClError = FALSE;
    772   while (fgets (Line, sizeof (Line), Fptr) != NULL) {
    773     Ret = sscanf (Line, "Note: including file: %s %c", IncludeFileName, &Char);
    774     if (Ret == 2) {
    775       //
    776       // There is space in include file name. It's VS header file. Ignore it.
    777       //
    778       continue;
    779     } else if ( Ret != 1) {
    780       //
    781       // Cl error info, print it
    782       // the tool will return error code to stop the nmake
    783       //
    784       ClError = TRUE;
    785       printf ("%s", Line);
    786       continue;
    787     }
    788 
    789     //
    790     // If we're not doing duplicates, and we've already seen this filename,
    791     // then continue
    792     //
    793     if (mGlobals.NoDupes) {
    794       for (ListPtr = ProcessedFiles->Next; ListPtr != NULL; ListPtr = ListPtr->Next) {
    795         if (_stricmp (IncludeFileName, ListPtr->Str) == 0) {
    796           break;
    797         }
    798       }
    799       //
    800       // If we found a match, we're done. If we didn't, create a new element
    801       // and add it to the list.
    802       //
    803       if (ListPtr != NULL) {
    804         //
    805         // Print a message if verbose mode
    806         //
    807         if (mGlobals.Verbose) {
    808           DebugMsg (NULL, 0, 0, IncludeFileName, "duplicate include -- not processed again");
    809         }
    810 
    811         continue;
    812       }
    813 
    814       ListPtr       = malloc (sizeof (STRING_LIST));
    815       ListPtr->Str  = malloc (strlen (IncludeFileName) + 1);
    816       strcpy (ListPtr->Str, IncludeFileName);
    817       ListPtr->Next         = ProcessedFiles->Next;
    818       ProcessedFiles->Next  = ListPtr;
    819     }
    820 
    821     PrintDependency (TargetFileName, IncludeFileName);
    822   }
    823 
    824   fclose (Fptr);
    825 
    826   if (ClError) {
    827     Error (NULL, 0, 0, NULL, "cl error");
    828     return STATUS_ERROR;
    829   } else {
    830     return STATUS_SUCCESS;
    831   }
    832 }
    833 
    834 static
    835 void
    836 PrintDependency (
    837   INT8    *TargetFileName,
    838   INT8    *DependentFile
    839   )
    840 /*++
    841 
    842 Routine Description:
    843 
    844   Given a target (.obj) file name, and a dependent file name, do any string
    845   substitutions (per the command line options) on the file names, then
    846   print the dependency line of form:
    847 
    848   TargetFileName : DependentFile
    849 
    850 Arguments:
    851 
    852   TargetFileName - build target file name
    853   DependentFile  - file on which TargetFileName depends
    854 
    855 Returns:
    856 
    857   None
    858 
    859 --*/
    860 {
    861   INT8  Str[MAX_PATH];
    862 
    863   //
    864   // Go through the symbols and do replacements
    865   //
    866   strcpy (Str, TargetFileName);
    867   ReplaceSymbols (Str, sizeof (Str));
    868   fprintf (mGlobals.OutFptr, "%s : ", Str);
    869   strcpy (Str, DependentFile);
    870   ReplaceSymbols (Str, sizeof (Str));
    871   fprintf (mGlobals.OutFptr, "%s\n", Str);
    872   //
    873   // Add pseudo target to avoid incremental build failure when the file is deleted
    874   //
    875   fprintf (mGlobals.OutFptr, "%s : \n", Str);
    876 }
    877 
    878 static
    879 void
    880 ReplaceSymbols (
    881   INT8    *Str,
    882   UINT32  StrSize
    883   )
    884 {
    885   SYMBOL  *Sym;
    886   INT8    StrCopy[MAX_LINE_LEN];
    887   INT8    *From;
    888   INT8    *To;
    889   BOOLEAN Replaced;
    890 
    891   //
    892   // Go through the entire string to look for replacement strings at
    893   // every position.
    894   //
    895   From  = Str;
    896   To    = StrCopy;
    897   while (*From) {
    898     //
    899     // Copy the character
    900     //
    901     *To       = *From;
    902     Replaced  = FALSE;
    903     //
    904     // Go through each symbol and try to find a string substitution
    905     //
    906     Sym = mGlobals.SymbolTable;
    907     while (Sym != NULL) {
    908       if (_strnicmp (From, Sym->Value, strlen (Sym->Value)) == 0) {
    909         //
    910         // Replace the string, then advance the pointers past the
    911         // replaced strings
    912         //
    913         strcpy (To, Sym->Name);
    914         To += strlen (Sym->Name);
    915         From += strlen (Sym->Value);
    916         Replaced = TRUE;
    917         //
    918         // Break from the while()
    919         //
    920         break;
    921       } else {
    922         Sym = Sym->Next;
    923       }
    924     }
    925 
    926     if (!Replaced) {
    927       From++;
    928       To++;
    929     }
    930   }
    931   //
    932   // Null terminate, and return it
    933   //
    934   *To = 0;
    935   if (strlen (StrCopy) < StrSize) {
    936     strcpy (Str, StrCopy);
    937   }
    938 }
    939 //
    940 // Given a filename, try to find it along the include paths.
    941 //
    942 static
    943 FILE *
    944 FindFile (
    945   INT8              *FileName,
    946   UINT32            FileNameLen,
    947   FILE_SEARCH_TYPE  FileSearchType
    948   )
    949 {
    950   FILE        *Fptr;
    951   STRING_LIST *List;
    952   STRING_LIST *SubDir;
    953   INT8        FullFileName[MAX_PATH * 2];
    954 
    955   //
    956   // Traverse the list of paths and try to find the file
    957   //
    958   if (FileSearchType == SearchAllPaths) {
    959     List = mGlobals.ParentPaths;
    960     while (List != NULL) {
    961       //
    962       // Put the path and filename together
    963       //
    964       if (strlen (List->Str) + strlen (FileName) + 1 > sizeof (FullFileName)) {
    965         Error (
    966           __FILE__,
    967           __LINE__,
    968           0,
    969           "application error",
    970           "cannot concatenate '%s' + '%s'",
    971           List->Str,
    972           FileName
    973           );
    974         return NULL;
    975       }
    976       //
    977       // Append the filename to this include path and try to open the file.
    978       //
    979       strcpy (FullFileName, List->Str);
    980       strcat (FullFileName, FileName);
    981       if ((Fptr = fopen (FullFileName, "r")) != NULL) {
    982         //
    983         // Return the file name
    984         //
    985         if (FileNameLen <= strlen (FullFileName)) {
    986           Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
    987           //
    988           // fprintf (stdout, "File length > %d: %s\n", FileNameLen, FullFileName);
    989           //
    990           return NULL;
    991         }
    992 
    993         strcpy (FileName, FullFileName);
    994         return Fptr;
    995       }
    996 
    997       List = List->Next;
    998     }
    999   }
   1000 
   1001   List = mGlobals.IncludePaths;
   1002   while (List != NULL) {
   1003     //
   1004     // Put the path and filename together
   1005     //
   1006     if (strlen (List->Str) + strlen (FileName) + 1 > sizeof (FullFileName)) {
   1007       Error (
   1008         __FILE__,
   1009         __LINE__,
   1010         0,
   1011         "application error",
   1012         "cannot concatenate '%s' + '%s'",
   1013         List->Str,
   1014         FileName
   1015         );
   1016       return NULL;
   1017     }
   1018     //
   1019     // Append the filename to this include path and try to open the file.
   1020     //
   1021     strcpy (FullFileName, List->Str);
   1022     strcat (FullFileName, FileName);
   1023     if ((Fptr = fopen (FullFileName, "r")) != NULL) {
   1024       //
   1025       // Return the file name
   1026       //
   1027       if (FileNameLen <= strlen (FullFileName)) {
   1028         Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
   1029         //
   1030         // fprintf (stdout, "File length > %d: %s\n", FileNameLen, FullFileName);
   1031         //
   1032         return NULL;
   1033       }
   1034 
   1035       strcpy (FileName, FullFileName);
   1036       return Fptr;
   1037     }
   1038     //
   1039     // Didn't find it there. Now try this directory with every subdirectory
   1040     // the user specified on the command line
   1041     //
   1042     for (SubDir = mGlobals.SubDirs; SubDir != NULL; SubDir = SubDir->Next) {
   1043       strcpy (FullFileName, List->Str);
   1044       strcat (FullFileName, SubDir->Str);
   1045       strcat (FullFileName, FileName);
   1046       if ((Fptr = fopen (FullFileName, "r")) != NULL) {
   1047         //
   1048         // Return the file name
   1049         //
   1050         if (FileNameLen <= strlen (FullFileName)) {
   1051           Error (__FILE__, __LINE__, 0, "application error", "internal path name of insufficient length");
   1052           return NULL;
   1053         }
   1054 
   1055         strcpy (FileName, FullFileName);
   1056         return Fptr;
   1057       }
   1058     }
   1059 
   1060     List = List->Next;
   1061   }
   1062   //
   1063   // Not found
   1064   //
   1065   return NULL;
   1066 }
   1067 //
   1068 // Process the command-line arguments
   1069 //
   1070 static
   1071 STATUS
   1072 ProcessArgs (
   1073   int   Argc,
   1074   char  *Argv[]
   1075   )
   1076 {
   1077   STRING_LIST *NewList;
   1078   STRING_LIST *LastIncludePath;
   1079   STRING_LIST *LastSourceFile;
   1080   SYMBOL      *Symbol;
   1081 
   1082   //
   1083   // Clear our globals
   1084   //
   1085   memset ((char *) &mGlobals, 0, sizeof (mGlobals));
   1086   mGlobals.NoDupes = TRUE;
   1087   //
   1088   // Skip program name
   1089   //
   1090   Argc--;
   1091   Argv++;
   1092   //
   1093   // Initialize locals
   1094   //
   1095   LastIncludePath = NULL;
   1096   LastSourceFile  = NULL;
   1097   //
   1098   // Process until no more args
   1099   //
   1100   while (Argc) {
   1101     //
   1102     // -i path    add include search path
   1103     //
   1104     if (_stricmp (Argv[0], "-i") == 0) {
   1105       //
   1106       // check for one more arg
   1107       //
   1108       if (Argc > 1) {
   1109         //
   1110         // Allocate memory for a new list element, fill it in, and
   1111         // add it to our list of include paths. Always make sure it
   1112         // has a "\" on the end of it.
   1113         //
   1114         NewList = malloc (sizeof (STRING_LIST));
   1115         if (NewList == NULL) {
   1116           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1117           return STATUS_ERROR;
   1118         }
   1119 
   1120         NewList->Next = NULL;
   1121         NewList->Str  = malloc (strlen (Argv[1]) + 2);
   1122         if (NewList->Str == NULL) {
   1123           free (NewList);
   1124           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1125           return STATUS_ERROR;
   1126         }
   1127 
   1128         strcpy (NewList->Str, Argv[1]);
   1129         if (NewList->Str[strlen (NewList->Str) - 1] != '\\') {
   1130           strcat (NewList->Str, "\\");
   1131         }
   1132         //
   1133         // Add it to the end of the our list of include paths
   1134         //
   1135         if (mGlobals.IncludePaths == NULL) {
   1136           mGlobals.IncludePaths = NewList;
   1137         } else {
   1138           LastIncludePath->Next = NewList;
   1139         }
   1140 
   1141         LastIncludePath = NewList;
   1142         //
   1143         // fprintf (stdout, "Added path: %s\n", NewList->Str);
   1144         //
   1145       } else {
   1146         Error (NULL, 0, 0, Argv[0], "option requires an include path");
   1147         Usage ();
   1148         return STATUS_ERROR;
   1149       }
   1150 
   1151       Argc--;
   1152       Argv++;
   1153     } else if (_stricmp (Argv[0], "-f") == 0) {
   1154       //
   1155       // Check for one more arg
   1156       //
   1157       if (Argc > 1) {
   1158         //
   1159         // Allocate memory for a new list element, fill it in, and
   1160         // add it to our list of source files.
   1161         //
   1162         NewList = malloc (sizeof (STRING_LIST));
   1163         if (NewList == NULL) {
   1164           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1165           return STATUS_ERROR;
   1166         }
   1167 
   1168         NewList->Next = NULL;
   1169         //
   1170         // Allocate space to replace ".c" with ".obj", plus null termination
   1171         //
   1172         NewList->Str = malloc (strlen (Argv[1]) + 5);
   1173         if (NewList->Str == NULL) {
   1174           free (NewList);
   1175           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1176           return STATUS_ERROR;
   1177         }
   1178 
   1179         strcpy (NewList->Str, Argv[1]);
   1180         if (mGlobals.SourceFiles == NULL) {
   1181           mGlobals.SourceFiles = NewList;
   1182         } else {
   1183           LastSourceFile->Next = NewList;
   1184         }
   1185 
   1186         LastSourceFile = NewList;
   1187       } else {
   1188         Error (NULL, 0, 0, Argv[0], "option requires a file name");
   1189         Usage ();
   1190         return STATUS_ERROR;
   1191       }
   1192 
   1193       Argc--;
   1194       Argv++;
   1195     } else if (_stricmp (Argv[0], "-s") == 0) {
   1196       //
   1197       // -s subdir    add subdirectory subdir to list of subdirecties to scan.
   1198       // Check for one more arg first.
   1199       //
   1200       if (Argc > 1) {
   1201         //
   1202         // Allocate memory for a new list element, fill it in, and
   1203         // add it to our list of subdirectory include paths. Always
   1204         // make sure it has a "\" on the end of it.
   1205         //
   1206         NewList = malloc (sizeof (STRING_LIST));
   1207         if (NewList == NULL) {
   1208           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1209           return STATUS_ERROR;
   1210         }
   1211 
   1212         NewList->Str = malloc (strlen (Argv[1]) + 2);
   1213         if (NewList->Str == NULL) {
   1214           free (NewList);
   1215           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1216           return STATUS_ERROR;
   1217         }
   1218 
   1219         strcpy (NewList->Str, Argv[1]);
   1220         if (NewList->Str[strlen (NewList->Str) - 1] != '\\') {
   1221           strcat (NewList->Str, "\\");
   1222         }
   1223 
   1224         NewList->Next     = mGlobals.SubDirs;
   1225         mGlobals.SubDirs  = NewList;
   1226       } else {
   1227         Error (NULL, 0, 0, Argv[0], "option requires a subdirectory name");
   1228         Usage ();
   1229         return STATUS_ERROR;
   1230       }
   1231 
   1232       Argc--;
   1233       Argv++;
   1234     } else if (_stricmp (Argv[0], "-sub") == 0) {
   1235       //
   1236       // -sub symname symvalue  to do string substitution in the output
   1237       //
   1238       if (Argc > 2) {
   1239         //
   1240         // Allocate memory for the symbol object
   1241         //
   1242         Symbol = malloc (sizeof (SYMBOL));
   1243         if (Symbol == NULL) {
   1244           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1245           return STATUS_ERROR;
   1246         }
   1247         //
   1248         // Allocate memory for the symbol name and value, then save copies
   1249         //
   1250         Symbol->Name = malloc (strlen (Argv[1]) + 1);
   1251         if (Symbol->Name == NULL) {
   1252           free (Symbol);
   1253           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1254           return STATUS_ERROR;
   1255         }
   1256 
   1257         strcpy (Symbol->Name, Argv[1]);
   1258         Symbol->Value = malloc (strlen (Argv[2]) + 1);
   1259         if (Symbol->Value == NULL) {
   1260           free (Symbol->Name);
   1261           free (Symbol);
   1262           Error (__FILE__, __LINE__, 0, "memory allocation failure", NULL);
   1263           return STATUS_ERROR;
   1264         }
   1265 
   1266         strcpy (Symbol->Value, Argv[2]);
   1267         //
   1268         // Add it to the list
   1269         //
   1270         Symbol->Next          = mGlobals.SymbolTable;
   1271         mGlobals.SymbolTable  = Symbol;
   1272       } else {
   1273         Error (NULL, 0, 0, Argv[0], "option requires a symbol name and value");
   1274         Usage ();
   1275         return STATUS_ERROR;
   1276       }
   1277       //
   1278       // Skip over args
   1279       //
   1280       Argc -= 2;
   1281       Argv += 2;
   1282     } else if (_stricmp (Argv[0], "-nosystem") == 0) {
   1283       mGlobals.NoSystem = TRUE;
   1284     } else if (_stricmp (Argv[0], "-nodupes") == 0) {
   1285       mGlobals.NoDupes = TRUE;
   1286     } else if (_stricmp (Argv[0], "-nodups") == 0) {
   1287       mGlobals.NoDupes = TRUE;
   1288     } else if (_stricmp (Argv[0], "-target") == 0) {
   1289       //
   1290       // -target TargetFileName  - Target object file (only one allowed right
   1291       // now) is TargetFileName rather than SourceFile.obj
   1292       //
   1293       if (Argc > 1) {
   1294         strcpy (mGlobals.TargetFileName, Argv[1]);
   1295       } else {
   1296         Error (NULL, 0, 0, Argv[0], "option requires a target file name");
   1297         Usage ();
   1298         return STATUS_ERROR;
   1299       }
   1300 
   1301       Argc--;
   1302       Argv++;
   1303     } else if (_stricmp (Argv[0], "-usesumdeps") == 0) {
   1304       //
   1305       // -usesumdeps Path - if we find an included file xxx.h, and file
   1306       // Path/xxx.dep exists, list Path/xxx.dep as a dependency rather than
   1307       // xxx.h and don't parse xxx.h. This allows you to create a dependency
   1308       // file for a commonly included file, and have its dependency file updated
   1309       // only if its included files are updated. Then anyone else including this
   1310       // common include file can simply have a dependency on that file's .dep file
   1311       // rather than on all the files included by it. Confusing enough?
   1312       //
   1313       mGlobals.UseSumDeps = 1;
   1314       if (Argc > 1) {
   1315         strcpy (mGlobals.SumDepsPath, Argv[1]);
   1316         //
   1317         // Add slash on end if not there
   1318         //
   1319         if (mGlobals.SumDepsPath[strlen (mGlobals.SumDepsPath) - 1] != '\\') {
   1320           strcat (mGlobals.SumDepsPath, "\\");
   1321         }
   1322       } else {
   1323         Error (NULL, 0, 0, Argv[0], "option requires path to summary dependency files");
   1324         Usage ();
   1325         return STATUS_ERROR;
   1326       }
   1327 
   1328       Argc--;
   1329       Argv++;
   1330 
   1331     } else if (_stricmp (Argv[0], "-o") == 0) {
   1332       //
   1333       // -o OutputFileName    - specify an output filename for dependency list
   1334       // check for one more arg
   1335       //
   1336       if (Argc > 1) {
   1337         mGlobals.OutFileName = Argv[1];
   1338         //
   1339         // Use temp file for output
   1340         // This can avoid overwriting previous existed dep file when error
   1341         // ocurred in this tool
   1342         //
   1343         sprintf (mGlobals.TmpFileName, "%s2", mGlobals.OutFileName);
   1344         //
   1345         // Try to open the temp file
   1346         //
   1347         if ((mGlobals.OutFptr = fopen (mGlobals.TmpFileName, "w")) == NULL) {
   1348           Error (NULL, 0, 0, mGlobals.TmpFileName, "could not open file for writing");
   1349           return STATUS_ERROR;
   1350         }
   1351       } else {
   1352         Error (NULL, 0, 0, Argv[0], "option requires output file name");
   1353         Usage ();
   1354         return STATUS_ERROR;
   1355       }
   1356 
   1357       Argc--;
   1358       Argv++;
   1359     } else if (_stricmp (Argv[0], "-v") == 0) {
   1360       mGlobals.Verbose = TRUE;
   1361     } else if (_stricmp (Argv[0], "-neverfail") == 0) {
   1362       mGlobals.NeverFail = TRUE;
   1363     } else if (_stricmp (Argv[0], "-q") == 0) {
   1364       mGlobals.QuietMode = TRUE;
   1365     } else if (_stricmp (Argv[0], "-ignorenotfound") == 0) {
   1366       mGlobals.IgnoreNotFound = TRUE;
   1367     } else if (_stricmp (Argv[0], "-asm") == 0) {
   1368       if (mGlobals.IsCl) {
   1369         Error (NULL, 0, 0, Argv[0], "option conflict with -cl");
   1370         return STATUS_ERROR;
   1371       }
   1372       mGlobals.IsAsm = TRUE;
   1373     } else if (_stricmp (Argv[0], "-cl") == 0) {
   1374       if (mGlobals.IsAsm) {
   1375         Error (NULL, 0, 0, Argv[0], "option conflict with -asm");
   1376         return STATUS_ERROR;
   1377       }
   1378       mGlobals.IsCl = TRUE;
   1379     } else if ((_stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) {
   1380       Usage ();
   1381       return STATUS_ERROR;
   1382     } else {
   1383       Error (NULL, 0, 0, Argv[0], "unrecognized option");
   1384       Usage ();
   1385       return STATUS_ERROR;
   1386     }
   1387 
   1388     Argc--;
   1389     Argv++;
   1390   }
   1391   //
   1392   // Had to specify at least one source file
   1393   //
   1394   if (mGlobals.SourceFiles == NULL) {
   1395     Error (NULL, 0, 0, "must specify one source file name", NULL);
   1396     Usage ();
   1397     return STATUS_ERROR;
   1398   }
   1399   //
   1400   // Assume output to stdout if not specified
   1401   //
   1402   if (mGlobals.OutFptr == NULL) {
   1403     mGlobals.OutFptr = stdout;
   1404   }
   1405 
   1406   return STATUS_SUCCESS;
   1407 }
   1408 //
   1409 // Free the global string lists we allocated memory for
   1410 //
   1411 static
   1412 void
   1413 FreeLists (
   1414   VOID
   1415   )
   1416 {
   1417   STRING_LIST *Temp;
   1418   SYMBOL      *NextSym;
   1419 
   1420   //
   1421   // printf ("Free lists.....");
   1422   //
   1423   // Traverse the include paths, freeing each
   1424   // printf ("freeing include paths\n");
   1425   //
   1426   while (mGlobals.IncludePaths != NULL) {
   1427     Temp = mGlobals.IncludePaths->Next;
   1428     //
   1429     // printf ("Freeing include path string '%s' at 0x%X\n",
   1430     //  mGlobals.IncludePaths->Str, (int)(mGlobals.IncludePaths->Str));
   1431     //
   1432     free (mGlobals.IncludePaths->Str);
   1433     //
   1434     // printf ("Freeing include path object at 0x%X\n", (int)(mGlobals.IncludePaths));
   1435     //
   1436     free (mGlobals.IncludePaths);
   1437     mGlobals.IncludePaths = Temp;
   1438   }
   1439   //
   1440   // Traverse the source files, freeing each
   1441   //
   1442   while (mGlobals.SourceFiles != NULL) {
   1443     Temp = mGlobals.SourceFiles->Next;
   1444     free (mGlobals.SourceFiles->Str);
   1445     free (mGlobals.SourceFiles);
   1446     mGlobals.SourceFiles = Temp;
   1447   }
   1448   //
   1449   // Traverse the subdirectory list, freeing each
   1450   //
   1451   while (mGlobals.SubDirs != NULL) {
   1452     Temp = mGlobals.SubDirs->Next;
   1453     free (mGlobals.SubDirs->Str);
   1454     free (mGlobals.SubDirs);
   1455     mGlobals.SubDirs = Temp;
   1456   }
   1457   //
   1458   // Free the symbol table
   1459   //
   1460   while (mGlobals.SymbolTable != NULL) {
   1461     NextSym = mGlobals.SymbolTable->Next;
   1462     free (mGlobals.SymbolTable->Name);
   1463     free (mGlobals.SymbolTable->Value);
   1464     mGlobals.SymbolTable = NextSym;
   1465   }
   1466   //
   1467   // printf ("done\n");
   1468   //
   1469 }
   1470 
   1471 static
   1472 void
   1473 Usage (
   1474   VOID
   1475   )
   1476 /*++
   1477 
   1478 Routine Description:
   1479 
   1480   Print usage information for this utility.
   1481 
   1482 Arguments:
   1483 
   1484   None.
   1485 
   1486 Returns:
   1487 
   1488   Nothing.
   1489 
   1490 --*/
   1491 {
   1492   int         Index;
   1493   const char  *Str[] = {
   1494     UTILITY_NAME" "UTILITY_VERSION" - Intel Make Dependencies Utility",
   1495     "  Copyright (C), 2004 - 2008 Intel Corporation",
   1496 
   1497 #if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
   1498     "  Built from "UTILITY_BUILD", project of "UTILITY_VENDOR,
   1499 #endif
   1500     "",
   1501     "Usage:",
   1502     "  "UTILITY_NAME" [OPTION]...",
   1503     "Options:",
   1504     "  -h or -?         for this help information",
   1505     "  -f SourceFile    add SourceFile to list of files to scan",
   1506     "  -i IncludePath   add IncludePath to list of search paths",
   1507     "  -o OutputFile    write output dependencies to OutputFile",
   1508     "  -s SubDir        for each IncludePath, also search IncludePath\\SubDir",
   1509     "  -v               for verbose output",
   1510     "  -ignorenotfound  don't warn for files not found",
   1511     "  -target Target   for single SourceFile, target is Target, not SourceFile.obj",
   1512     "  -q               quiet mode to not report files not found if ignored",
   1513     "  -sub sym str     replace all occurrances of 'str' with 'sym' in the output",
   1514     "  -nosystem        not process system <include> files",
   1515     "  -neverfail       always return a success return code",
   1516     //
   1517     //    "  -nodupes         keep track of include files, don't rescan duplicates",
   1518     //
   1519     "  -usesumdeps path use summary dependency files in 'path' directory.",
   1520     "  -asm             The SourceFiles are assembler files",
   1521     "  -cl              The SourceFiles are the output of cl with /showIncludes",
   1522     NULL
   1523   };
   1524   for (Index = 0; Str[Index] != NULL; Index++) {
   1525     fprintf (stdout, "%s\n", Str[Index]);
   1526   }
   1527 }
   1528