/*++

Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials                          
are licensed and made available under the terms and conditions of the BSD License         
which accompanies this distribution.  The full text of the license may be found at        
http://opensource.org/licenses/bsd-license.php                                            
                                                                                          
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             

Module Name:
  
  ProcessDsc.c

Abstract:

  Main module for the ProcessDsc utility.

--*/

#include <windows.h>  // for GetShortPathName()
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include <direct.h>   // for _mkdir()
#include <errno.h>
#include <stdlib.h>   // for getenv()
#include <shlwapi.h>  // for PathCanonicalize()
#include "DSCFile.h"
#include "MultiThread.h"
#include "FWVolume.h"
#include "Exceptions.h"
#include "Common.h"

#include "EfiUtilityMsgs.h"
#include "TianoBind.h"

#define UTILITY_NAME    "ProcessDsc"
#define UTILITY_VERSION "v1.0"

//
// Common symbol name definitions. For example, the user can reference
// $(BUILD_DIR) in their DSC file and we will expand it for them (usually).
// I've defined the equivalents here in case we want to change the name the
// user references, in which case we just change the string value here and
// our code still works.
//
#define BUILD_DIR                       "BUILD_DIR"
#define EFI_SOURCE                      "EFI_SOURCE"
#define DEST_DIR                        "DEST_DIR"
#define SOURCE_DIR                      "SOURCE_DIR"
#define LIB_DIR                         "LIB_DIR"
#define BIN_DIR                         "BIN_DIR"
#define OUT_DIR                         "OUT_DIR"
#define INF_FILENAME                    "INF_FILENAME"
#define SOURCE_RELATIVE_PATH            "SOURCE_RELATIVE_PATH"
#define SOURCE_BASE_NAME                "SOURCE_BASE_NAME"
#define SOURCE_FILE_NAME                "SOURCE_FILE_NAME"    // c:\FullPath\File.c
#define PROCESSOR                       "PROCESSOR"
#define FV                              "FV"
#define BASE_NAME                       "BASE_NAME"
#define GUID                            "GUID"
#define FILE_GUID                       "FILE_GUID"
#define COMPONENT_TYPE_FILE             "FILE"
#define BUILD_TYPE                      "BUILD_TYPE"
#define FFS_EXT                         "FFS_EXT"             // FV_EXT is deprecated -- extension of FFS file
#define MAKEFILE_NAME                   "MAKEFILE_NAME"       // name of component's output makefile
#define PLATFORM                        "PLATFORM"            // for more granularity
#define PACKAGE_FILENAME                "PACKAGE_FILENAME"
#define PACKAGE                         "PACKAGE"
#define PACKAGE_TAG                     "PACKAGE_TAG"         // alternate name to PACKAGE
#define SHORT_NAMES                     "SHORT_NAMES"         // for 8.3 names of symbols
#define APRIORI                         "APRIORI"             // to add to apriori list
#define OPTIONAL_COMPONENT              "OPTIONAL"            // define as non-zero for optional INF files
#define SOURCE_SELECT                   "SOURCE_SELECT"       // say SOURCE_SELECT=smm,common to select INF sources
#define NONFFS_FV                       "NONFFS_FV"           // for non-FFS FV such as working & spare block FV
#define SKIP_FV_NULL                    "SKIP_FV_NULL"        // define as nonzero to not build components with FV=NULL
#define SOURCE_COMPILE_TYPE             "SOURCE_COMPILE_TYPE" // to build a source using a custom build section in the DSC file
#define SOURCE_FILE_EXTENSION           "SOURCE_FILE_EXTENSION"
#define COMPILE_SELECT                  "COMPILE_SELECT"
#define SOURCE_OVERRIDE_PATH            "SOURCE_OVERRIDE_PATH"  // get source files from here first
#define MAKEFILE_OUT_SECTION_NAME       "makefile.out"
#define COMMON_SECTION_NAME             "common"                // shared files or functionality
#define NMAKE_SECTION_NAME              "nmake"
#define SOURCES_SECTION_NAME            "sources"
#define COMPONENTS_SECTION_NAME         "components"
#define INCLUDE_SECTION_NAME            "includes"
#define DEFINES_SECTION_NAME            "defines"
#define LIBRARIES_SECTION_NAME          "libraries"
#define LIBRARIES_PLATFORM_SECTION_NAME "libraries.platform"
#define MAKEFILE_SECTION_NAME           "makefile"
#define COMPONENT_TYPE                  "component_type"
#define PLATFORM_STR                    "\\platform\\"          // to determine EFI_SOURCE
#define MAKEFILE_OUT_NAME               "makefile.out"          // if not specified on command line
#define MODULE_MAKEFILE_NAME            "module.mak"            // record all module makefile targets
#define MODULE_NAME_FILE                "module.list"           // record all module names defined in the dsc file
#define GLOBAL_LINK_LIB_NAME            "CompilerStub"          // Lib added in link option, maybe removed in the future
#define MODULE_BASE_NAME_WIDTH          25                      // Width for module name output

//
// When a symbol is defined as "NULL", it gets saved in the symbol table as a 0-length
// string. Use this macro to detect if a symbol has been defined this way.
//
#define IS_NULL_SYMBOL_VALUE(var) ((var != NULL) && (strlen (var) == 0))

//
// Defines for file types
//
#define FILETYPE_UNKNOWN  0
#define FILETYPE_C        1
#define FILETYPE_ASM      2
#define FILETYPE_S        3
#define FILETYPE_VFR      4
#define FILETYPE_INC      5
#define FILETYPE_H        6
#define FILETYPE_I        7


typedef struct {
  INT8  *Extension;         // file extension
  INT8  *BuiltExtension;
  INT8  FileFlags;
  int   FileType;
} FILETYPE;

//
// Define masks for the FileFlags field
//
#define FILE_FLAG_INCLUDE 0x01
#define FILE_FLAG_SOURCE  0x02

//
// This table describes a from-to list of files. For
// example, when a ".c" is built, it results in a ".obj" file.
//
static const FILETYPE mFileTypes[] = {
  {
    ".c",
    ".obj",
    FILE_FLAG_SOURCE,
    FILETYPE_C
  },
  {
    ".asm",
    ".obj",
    FILE_FLAG_SOURCE,
    FILETYPE_ASM
  },
  {
    ".s",
    ".obj",
    FILE_FLAG_SOURCE,
    FILETYPE_S
  },
  {
    ".vfr",
    ".obj",
    FILE_FLAG_SOURCE,
    FILETYPE_VFR
  },  // actually *.vfr -> *.c -> *.obj
  {
    ".h",
    NULL,
    FILE_FLAG_INCLUDE,
    FILETYPE_H
  },
  {
    ".inc",
    NULL,
    FILE_FLAG_INCLUDE,
    FILETYPE_INC
  },
  {
    ".i",
    NULL,
    FILE_FLAG_INCLUDE,
    FILETYPE_I
  },
  {
    NULL,
    NULL,
    0,
    0
  }
};

//
// Structure to split up a file into its different parts.
//
typedef struct {
  INT8  Drive[3];
  INT8  *Path;
  INT8  *BaseName;
  INT8  *Extension;
  int   ExtensionCode;
} FILE_NAME_PARTS;

//
// Maximum length for any line in any file after symbol expansion
//
#define MAX_EXP_LINE_LEN  (MAX_LINE_LEN * 2)

//
// Linked list to keep track of all symbols
//
typedef struct _SYMBOL {
  struct _SYMBOL  *Next;
  int             Type; // local or global symbol
  INT8            *Name;
  INT8            *Value;
} SYMBOL;

//
// Module globals for multi-thread build
//
static BUILD_ITEM  **mCurrentBuildList;          // build list currently handling
static BUILD_ITEM  *mCurrentBuildItem;           // build item currently handling

//
// Define masks for the build targets
//
#define BUILD_TARGET_COMPONENTS 0x01
#define BUILD_TARGET_LIBRARIES  0x02
#define BUILD_TARGET_FVS        0x04
#define BUILD_TARGET_ALL        0xff


//
// This structure is used to save globals
//
struct {
  INT8                *DscFilename;
  SYMBOL              *Symbol;
  INT8                MakefileName[MAX_PATH]; // output makefile name
  INT8                XRefFileName[MAX_PATH];
  INT8                GuidDatabaseFileName[MAX_PATH];
  INT8                ModuleMakefileName[MAX_PATH];
  FILE                *MakefileFptr;
  FILE                *ModuleMakefileFptr;
  SYMBOL              *ModuleList;
  SYMBOL              *OutdirList;
  UINT32              Verbose;
  UINT32              ThreadNumber;
  UINT32              BuildTarget;
  BUILD_ITEM          *LibraryList;
  COMPONENTS_ITEM     *ComponentsList;
} gGlobals;

//
// This gets dumped to the head of makefile.out
//
static const INT8 *MakefileHeader[] = {
  "#/*++",
  "#",
  "#  DO NOT EDIT",
  "#  File auto-generated by build utility",
  "#",
  "#  Module Name:",
  "#",
  "#    makefile",
  "#",
  "#  Abstract:",
  "#",
  "#    Auto-generated makefile for building of EFI components/libraries",
  "#",
  "#--*/",
  "",
  NULL
};

//
// Function prototypes
//
static
int
ProcessOptions (
  int  Argc,
  INT8 *Argv[]
  );

static
void
Usage (
  VOID
  );

static
INT8              *
StripLine (
  INT8 *Line
  );

static
STATUS
ParseGuidDatabaseFile (
  INT8 *FileName
  );

#define DSC_SECTION_TYPE_COMPONENTS         0
#define DSC_SECTION_TYPE_LIBRARIES          1
#define DSC_SECTION_TYPE_PLATFORM_LIBRARIES 2

static
int
ProcessSectionComponents (
  DSC_FILE *DscFile,
  int      DscSectionType,
  int      Instance
  );
static
int
ProcessComponentFile (
  DSC_FILE  *DscFile,
  INT8      *Line,
  int       DscSectionType,
  int       Instance
  );
static
int
ProcessIncludeFiles (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  );
static

int
ProcessIncludeFilesSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  );

//
// Mode flags for processing source files
//
#define SOURCE_MODE_BUILD_COMMANDS  0x01
#define SOURCE_MODE_SOURCE_FILES    0x02

static
int
ProcessSourceFiles (
  DSC_FILE  *DSCFile,
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  UINT32    Mode
  );

static
int
ProcessSourceFilesSection (
  DSC_FILE  *DSCFile,
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName,
  UINT32    Mode
  );

static
int
ProcessObjects (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  );

static
int
ProcessObjectsSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  );

static
int
ProcessLibs (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  );

static
int
ProcessLibsSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  );

static
int
ProcessIncludesSection (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  );

static
int
ProcessIncludesSectionSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  );

static
int
ProcessINFNMakeSection (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  );

static
int
ProcessINFDefinesSection (
  DSC_FILE  *ComponentFile
  );

static
int
ProcessINFDefinesSectionSingle (
  DSC_FILE  *ComponentFile,
  INT8      *SectionName
  );

static
int
ProcessSectionLibraries (
  DSC_FILE  *DscFile,
  long      Offset
  );

static
int
ProcessDSCDefinesSection (
  DSC_FILE *DscFile
  );

static
int
SetSymbolType (
  INT8  *SymbolName,
  INT8  Type
  );

static
int
RemoveLocalSymbols (
  VOID
  );

static
int
RemoveFileSymbols (
  VOID
  );

static
int
RemoveSymbol (
  INT8   *Name,
  INT8   SymbolType
  );

static
int
SetFileExtension (
  INT8 *FileName,
  INT8 *Extension
  );

static
int
GetSourceFileType (
  INT8 *FileName
  );

static
int
IsIncludeFile (
  INT8 *FileName
  );

static
int
WriteCompileCommands (
  DSC_FILE *DscFile,
  FILE     *MakeFptr,
  INT8     *FileName,
  INT8     *Processor
  );

static
int
WriteCommonMakefile (
  DSC_FILE *DscFile,
  FILE     *MakeFptr,
  INT8     *Processor
  );

static
int
WriteComponentTypeBuildCommands (
  DSC_FILE *DscFile,
  FILE     *MakeFptr,
  INT8     *SectionName
  );

static
void
StripTrailingSpaces (
  INT8 *Str
  );

static
void
FreeFileParts (
  FILE_NAME_PARTS *FP
  );

static
FILE_NAME_PARTS   *
GetFileParts (
  INT8 *FileName
  );

static
SYMBOL            *
FreeSymbols (
  SYMBOL *Syms
  );

static
int
GetEfiSource (
  VOID
  );

static
int
CreatePackageFile (
  DSC_FILE          *DSCFile
  );

static
INT8              *
BuiltFileExtension (
  INT8      *SourceFileName
  );

static
void
SmartFree (
  SMART_FILE  *SmartFile
  );

static 
int
AddModuleName (
  SYMBOL  **SymbolList,
  INT8    *ModuleName,
  INT8    *InfName
  );

static 
void
ReplaceSlash (
  INT8    *Path
  );

/*****************************************************************************/
int
main (
  int   Argc,
  INT8  *Argv[]
  )
/*++

Routine Description:
  
  Main utility entry point.

Arguments:

  Argc - Standard app entry point args.
  Argv - Standard app entry point args.

Returns:

  0  if successful
  non-zero otherwise

--*/
{
  int                   i;
  DSC_FILE              DSCFile;
  SECTION               *Sect;
  INT8                  Line[MAX_LINE_LEN];
  INT8                  ExpLine[MAX_LINE_LEN];
  INT8                  *BuildDir;
  INT8                  *EMsg;
  FILE                  *FpModule;
  SYMBOL                *TempSymbol;
  COMPONENTS_ITEM       *TempComponents;

  SetUtilityName (UTILITY_NAME);

  InitExceptions ();

  DSCFileInit (&DSCFile);
  //
  // Initialize the firmware volume data
  //
  CFVConstructor ();
  //
  // Exception handling for this block of code.
  //
  TryException ();
  //
  // Process command-line options.
  //
  if (ProcessOptions (Argc, Argv)) {
    EMsg = CatchException ();
    if (EMsg != NULL) {
      fprintf (stderr, "%s\n", EMsg);
    }

    return STATUS_ERROR;
  }
  //
  // Parse the GUID database file if specified
  //
  if (gGlobals.GuidDatabaseFileName[0] != 0) {
    ParseGuidDatabaseFile (gGlobals.GuidDatabaseFileName);
  }
  //
  // Set the output cross-reference file if applicable
  //
  if (gGlobals.XRefFileName[0]) {
    CFVSetXRefFileName (gGlobals.XRefFileName);
  }

  //
  // Now get the EFI_SOURCE directory which we use everywhere.
  //
  if (GetEfiSource ()) {
    return STATUS_ERROR;
  }
    
  //
  // Pre-process the DSC file to get section info.
  //
  if (DSCFileSetFile (&DSCFile, gGlobals.DscFilename) != 0) {
    goto ProcessingError;
  }

  //
  // Set output makefile name for single module build 
  //
  strcpy (gGlobals.ModuleMakefileName, MODULE_MAKEFILE_NAME);

  //
  // Try to open all final output makefiles
  //
  if ((gGlobals.MakefileFptr = fopen (gGlobals.MakefileName, "w")) == NULL) {
    Error (NULL, 0, 0, gGlobals.MakefileName, "failed to open output makefile for writing");
    goto ProcessingError;
  }
  if ((gGlobals.ModuleMakefileFptr = fopen (gGlobals.ModuleMakefileName, "w")) == NULL) {
    Error (NULL, 0, 0, gGlobals.ModuleMakefileName, "failed to open output makefile for writing");
    goto ProcessingError;
  }
    
  //
  // Write the header out to the makefiles
  //
  for (i = 0; MakefileHeader[i] != NULL; i++) {
    fprintf (gGlobals.MakefileFptr, "%s\n", MakefileHeader[i]);
    fprintf (gGlobals.ModuleMakefileFptr, "%s\n", MakefileHeader[i]);
  }

  //
  // Init global potint = NULL
  //
  gGlobals.ModuleList = NULL;
  gGlobals.OutdirList = NULL;
  
  //
  // Process the [defines] section in the DSC file to get any defines we need
  // elsewhere
  //
  ProcessDSCDefinesSection (&DSCFile);
  if (ExceptionThrown ()) {
    goto ProcessingError;
  }
  //
  // Write out the [makefile.out] section data to the output makefiles
  //
  Sect = DSCFileFindSection (&DSCFile, MAKEFILE_OUT_SECTION_NAME);
  if (Sect != NULL) {
    while (DSCFileGetLine (&DSCFile, Line, sizeof (Line)) != NULL) {
      ExpandSymbols (Line, ExpLine, sizeof (ExpLine), 0);
      //
      // Write the line to the output makefiles
      //
      fprintf (gGlobals.MakefileFptr, ExpLine);
      fprintf (gGlobals.ModuleMakefileFptr, ExpLine);
    }
  }
  
  //
  // Add a pseudo target for GLOBAL_LINK_LIB_NAME to avoid single module build 
  // failure when this lib is not used.
  //
  fprintf (gGlobals.ModuleMakefileFptr, "%sbuild ::\n\n", GLOBAL_LINK_LIB_NAME);

  fprintf (gGlobals.MakefileFptr, "libraries : \n");
  //
  // Process [libraries] section in the DSC file
  //
  Sect = DSCFileFindSection (&DSCFile, LIBRARIES_SECTION_NAME);
  if (Sect != NULL) {
    mCurrentBuildList = &gGlobals.LibraryList;
    ProcessSectionComponents (&DSCFile, DSC_SECTION_TYPE_LIBRARIES, 0);
  }

  if (ExceptionThrown ()) {
    goto ProcessingError;
  }
  //
  // Process [libraries.platform] section in the DSC file
  //
  Sect = DSCFileFindSection (&DSCFile, LIBRARIES_PLATFORM_SECTION_NAME);
  if (Sect != NULL) {
    mCurrentBuildList = &gGlobals.LibraryList;
    ProcessSectionComponents (&DSCFile, DSC_SECTION_TYPE_PLATFORM_LIBRARIES, 0);
  }

  fprintf (gGlobals.MakefileFptr, "\n");
  if (ExceptionThrown ()) {
    goto ProcessingError;
  }
  
  //
  // Process [components] section in the DSC file
  //
  Sect = DSCFileFindSection (&DSCFile, COMPONENTS_SECTION_NAME);
  if (Sect != NULL) {
    fprintf (gGlobals.MakefileFptr, "components_0 : \n");
    TempComponents    = AddComponentsItem (&gGlobals.ComponentsList);
    mCurrentBuildList = &TempComponents->BuildList;
    ProcessSectionComponents (&DSCFile, DSC_SECTION_TYPE_COMPONENTS, 0);
    fprintf (gGlobals.MakefileFptr, "\n");
  }

  if (ExceptionThrown ()) {
    goto ProcessingError;
  }
  //
  // Now cycle through all [components.1], [components.2], ....[components.n].
  // This is necessary to support building of firmware volumes that may contain
  // other encapsulated firmware volumes (ala capsules).
  //
  i = 1;
  while (1) {
    RemoveSymbol (FV, SYM_GLOBAL);
    sprintf (Line, "%s.%d", COMPONENTS_SECTION_NAME, i);
    Sect = DSCFileFindSection (&DSCFile, Line);
    if (Sect != NULL) {
      fprintf (gGlobals.MakefileFptr, "components_%d : \n", i);
      TempComponents    = AddComponentsItem (&gGlobals.ComponentsList);
      mCurrentBuildList = &TempComponents->BuildList;
      ProcessSectionComponents (&DSCFile, DSC_SECTION_TYPE_COMPONENTS, i);
      fprintf (gGlobals.MakefileFptr, "\n");
    } else {
      break;
    }

    if (ExceptionThrown ()) {
      goto ProcessingError;
    }

    i++;
  }

ProcessingError:
  EMsg = CatchException ();
  if (EMsg != NULL) {
    fprintf (stderr, "%s\n", EMsg);
    fprintf (stderr, "Processing aborted\n");
  }

  TryException ();
  //
  // Create the FV files if no fatal errors or errors
  //
  if (GetUtilityStatus () < STATUS_ERROR) {
    CFVWriteInfFiles (&DSCFile, gGlobals.MakefileFptr);
  }

  //
  // Write all module name into MODULE_NAME_FILE file.
  //
  if ((FpModule = fopen (MODULE_NAME_FILE, "w")) != NULL) {
    TempSymbol = gGlobals.ModuleList;
    while (TempSymbol != NULL) {
      fprintf (FpModule, " %-*s %s \n", MODULE_BASE_NAME_WIDTH, TempSymbol->Name, TempSymbol->Value);
      TempSymbol = TempSymbol->Next;
    }
    fclose (FpModule);
    FpModule = NULL;
  }

  //
  // Close the all the output makefiles
  //
  if (gGlobals.MakefileFptr != NULL) {
    fclose (gGlobals.MakefileFptr);
    gGlobals.MakefileFptr = NULL;
  }

  if (gGlobals.ModuleMakefileFptr != NULL) {
    fclose (gGlobals.ModuleMakefileFptr);
    gGlobals.ModuleMakefileFptr = NULL;
  }
  
  //
  // Start multi-thread build if ThreadNumber is specified and no error status
  //
  if ((gGlobals.ThreadNumber != 0) && (GetUtilityStatus () < STATUS_ERROR)) {
    BuildDir = GetSymbolValue (BUILD_DIR);
    if (gGlobals.BuildTarget & BUILD_TARGET_LIBRARIES) {
      if (StartMultiThreadBuild (&gGlobals.LibraryList, gGlobals.ThreadNumber, BuildDir) != 0) {
        Error (NULL, 0, 0, NULL, "Multi-thread build libraries failure");
        goto Cleanup;
      }
    }
    i = 0;
    TempComponents = gGlobals.ComponentsList;
    while (TempComponents != NULL) {
      if (gGlobals.BuildTarget & BUILD_TARGET_COMPONENTS) {
        if (StartMultiThreadBuild (&TempComponents->BuildList, gGlobals.ThreadNumber, BuildDir) != 0) {
          Error (NULL, 0, 0, NULL, "Multi-thread build components %d failure", i);
          goto Cleanup;
        }
      }
      if (gGlobals.BuildTarget & BUILD_TARGET_FVS) {
        sprintf (ExpLine, "nmake -nologo -f %s fvs_%d", gGlobals.MakefileName, i);
        _flushall ();
        if (system (ExpLine)) {
          Error (NULL, 0, 0, NULL, "Build FVs for components %d failure", i);
          goto Cleanup;
        }
      }
      i++;
      TempComponents = TempComponents->Next;
    }
  }

Cleanup:    
  //
  // Clean up
  //
  FreeBuildList (gGlobals.LibraryList);
  gGlobals.LibraryList = NULL;
  FreeComponentsList (gGlobals.ComponentsList);
  gGlobals.ComponentsList = NULL;
  FreeSymbols (gGlobals.ModuleList);
  gGlobals.ModuleList = NULL;
  FreeSymbols (gGlobals.OutdirList);
  gGlobals.OutdirList = NULL;
  FreeSymbols (gGlobals.Symbol);
  gGlobals.Symbol = NULL;
  CFVDestructor ();
  DSCFileDestroy (&DSCFile);

  EMsg = CatchException ();
  if (EMsg != NULL) {
    fprintf (stderr, "%s\n", EMsg);
    fprintf (stderr, "Processing aborted\n");
  }

  return GetUtilityStatus ();
}

static
int
ProcessSectionComponents (
  DSC_FILE  *DSCFile,
  int       DscSectionType,
  int       Instance
  )
/*++

Routine Description:
  
  Process the [components] or [libraries] section in the description file. We
  use this function for both since they're very similar. Here we just
  read each line from the section, and if it's valid, call a function to
  do the actual processing of the component description file.

Arguments:

  DSCFile        - structure containing section info on the description file
  DscSectionType - type of description section

Returns:

  0     if successful

--*/
{
  INT8  Line[MAX_LINE_LEN];
  INT8  Line2[MAX_EXP_LINE_LEN];
  INT8  *Cptr;

  //
  // Read lines while they're valid
  //
  while (DSCFileGetLine (DSCFile, Line, sizeof (Line)) != NULL) {
    //
    // Expand symbols on the line
    //
    if (ExpandSymbols (Line, Line2, sizeof (Line2), 0)) {
      return STATUS_ERROR;
    }
    //
    // Strip the line
    //
    Cptr = StripLine (Line2);
    if (*Cptr) {
      Message (2, "Processing component line: %s", Line2);
      if (ProcessComponentFile (DSCFile, Line2, DscSectionType, Instance) != 0) {
        return STATUS_ERROR;
      }
    }
  }

  return 0;
}

static
int
ProcessComponentFile (
  DSC_FILE  *DSCFile,
  INT8      *ArgLine,
  int       DscSectionType,
  int       Instance
  )
/*++

Routine Description:
  
  Given a line from the [components] or [libraries] section of the description
  file, process the line to extract the component's INF filename and 
  parameters. Then open the INF file and process it to create a corresponding
  makefile.

Arguments:

  DSCFile               The project DSC file info structure.
  Libs                  Indicates whether we're processing the [components]
                        section or the [libraries] section.
  ArgLine               The actual line from the DSC file. Looks something like
                        one of the following:

   dxe\drivers\vm\vm.dsc PROCESSOR=IA32 DEST_DIR=$(DEST_DIR)\xxx FV=FV1,FV2
   $(BUILD_DIR).\FvVariable.ffs COMPONENT_TYPE=FILE
   .\FvVariable.ffs COMPONENT_TYPE=FILE
   define  VAR1=value1 VAR2=value2

Returns:

  0 if successful

--*/
{
  FILE      *MakeFptr;
  FILE      *TempFptr;
  INT8      *Cptr;
  INT8      *name;
  INT8      *End;
  INT8      *TempCptr;
  INT8      FileName[MAX_PATH];
  INT8      ComponentFilePath[MAX_PATH];
  INT8      InLine[MAX_LINE_LEN];
  INT8      Line[MAX_LINE_LEN];
  INT8      *Processor;
  INT8      SymType;
  int       Len;
  int       ComponentCreated;
  int       ComponentFilePathAbsolute;
  int       DefineLine;
  DSC_FILE  ComponentFile;
  INT8      ComponentMakefileName[MAX_PATH];
  BOOLEAN   IsForFv;

  //
  // Now remove all local symbols
  //
  RemoveLocalSymbols ();
  //
  // Null out the file pointer in case we take an exception somewhere
  // and we need to close it only if we opened it.
  //
  MakeFptr                  = NULL;
  ComponentFilePathAbsolute = 0;
  ComponentCreated          = 0;
  //
  // Skip preceeding spaces on the line
  //
  while (isspace (*ArgLine) && (*ArgLine)) {
    ArgLine++;
  }
  //
  // Find the end of the component's filename and truncate the line at that
  // point. From here on out ArgLine is the name of the component filename.
  //
  Cptr = ArgLine;
  while (!isspace (*Cptr) && *Cptr) {
    Cptr++;
  }

  End = Cptr;
  if (*Cptr) {
    End++;
    *Cptr = 0;
  }
  //
  // Exception-handle processing of this component description file
  //
  TryException ();

  //
  // We also allow a component line format for defines of global symbols
  // instead of a component filename. In this case, the line looks like:
  // defines  x=abc y=yyy. Be nice and accept "define" and "defines" in a
  // case-insensitive manner. If it's defines, then make the symbols global.
  //
  if ((_stricmp (ArgLine, "define") == 0) || (_stricmp (ArgLine, "defines") == 0)) {
    SymType     = SYM_OVERWRITE | SYM_GLOBAL;
    DefineLine  = 1;
  } else {
    SymType     = SYM_OVERWRITE | SYM_LOCAL;
    DefineLine  = 0;
  }
  //
  // The rest of the component line from the DSC file should be defines
  //
  while (*End) {
    End = StripLine (End);
    if (*End) {
      //
      // If we're processing a "define abc=1 xyz=2" line, then set symbols
      // as globals per the SymType set above.
      //
      Len = AddSymbol (End, NULL, SymType);
      if (Len > 0) {
        End += Len;
      } else {
        Warning (NULL, 0, 0, ArgLine, "unrecognized option in description file");
        break;
      }
    }
  }

  //
  // If DEBUG_BREAK or EFI_BREAKPOINT is defined, then do a debug breakpoint.
  //
  if ((GetSymbolValue ("DEBUG_BREAK") != NULL) || (GetSymbolValue ("EFI_BREAKPOINT") != NULL)) {
    EFI_BREAKPOINT ();
  }
  
  //
  // If it's a define line, then we're done
  //
  if (DefineLine) {
    //
    // If there is NonFFS_FV, create the FVxxx.inf file
    // and include it in makefile.out. Remove the symbol
    // in order not to process it again next time
    //
    Cptr = GetSymbolValue (NONFFS_FV);
    if (Cptr != NULL) {
      NonFFSFVWriteInfFiles (DSCFile, Cptr);
      RemoveSymbol (NONFFS_FV, SYM_GLOBAL);
    }

    goto ComponentDone;
  }

  //
  // Expand symbols in the component description filename to expand the newly 
  // added local symbols
  //
  ExpandSymbols (ArgLine, Line, sizeof (Line), EXPANDMODE_NO_UNDEFS);
  
  //
  // If we have "c:\path\filename"
  //
  ReplaceSlash (Line);
  if (IsAbsolutePath (Line)) {
    ComponentFilePathAbsolute = 1;
  } else if (Line[0] == '.') {
    //
    // or if the path starts with ".", then it's build-dir relative.
    // Prepend $(BUILD_DIR) on the file name
    //
    sprintf (InLine, "%s\\%s", GetSymbolValue (BUILD_DIR), Line);
    strcpy (Line, InLine);
    ComponentFilePathAbsolute = 1;
  }
  
  //
  // Save the path from the component name for later. It may be relative or
  // absolute.
  //
  strcpy (ComponentFilePath, Line);
  Cptr = ComponentFilePath + strlen (ComponentFilePath) - 1;
  while ((*Cptr != '\\') && (Cptr != ComponentFilePath)) {
    Cptr--;
  }
  //
  // Terminate the path.
  //
  *Cptr = 0;
  
  //
  // Typically the given line is a component description filename. However we
  // also allow a FV filename (fvvariable.ffs COMPONENT_TYPE=FILE). If the
  // component type is "FILE", then add it to the FV list, create a package
  // file, and we're done.
  //
  Cptr = GetSymbolValue (COMPONENT_TYPE);
  if ((Cptr != NULL) && (strncmp (
                          Cptr,
                          COMPONENT_TYPE_FILE,
                          strlen (COMPONENT_TYPE_FILE)
                          ) == 0)) {
    if (ComponentFilePathAbsolute) {
      strcpy (InLine, Line);
    } else {
      sprintf (InLine, "%s\\%s", GetSymbolValue (EFI_SOURCE), Line);
    }
    CFVAddFVFile (
      InLine,
      Cptr,
      GetSymbolValue (FV),
      Instance,
      NULL,
      NULL,
      GetSymbolValue (APRIORI),
      NULL,
      NULL
      );
    goto ComponentDone;
  }

  //
  // Better have defined processor by this point.
  //
  Processor = GetSymbolValue (PROCESSOR);
  if (Processor == NULL) {
    Error (NULL, 0, 0, NULL, "PROCESSOR not defined for component %s", Line);
    return STATUS_ERROR;
  }

  //
  // The bin, out, and lib dirs are now = $(BUILD_DIR)/$(PROCESSOR). Set them.
  // Don't flag them as file paths (required for short 8.3 filenames) since
  // they're defined using the BUILD_DIR macro.
  //
  sprintf (InLine, "$(BUILD_DIR)\\%s", Processor);
  AddSymbol (BIN_DIR, InLine, SYM_LOCAL);
  AddSymbol (OUT_DIR, InLine, SYM_LOCAL);
  AddSymbol (LIB_DIR, InLine, SYM_LOCAL);
  //
  // See if it's been destined for an FV. It's possible to not be in an
  // FV if they just want to build it.
  //
  Cptr = GetSymbolValue (FV);
  if ((Cptr != NULL) && !IS_NULL_SYMBOL_VALUE (Cptr)) {
    IsForFv = TRUE;
  } else {
    IsForFv = FALSE;
  }
  //
  // As an optimization, if they've defined SKIP_FV_NULL as non-zero, and
  // the component is not destined for an FV, then skip it.
  // Since libraries are never intended for firmware volumes, we have to
  // build all of them.
  //
  if ((DscSectionType == DSC_SECTION_TYPE_COMPONENTS) && (IsForFv == FALSE)) {
    if ((GetSymbolValue (SKIP_FV_NULL) != NULL) && (atoi (GetSymbolValue (SKIP_FV_NULL)) != 0)) {
      Message (0, "%s not being built (FV=NULL)", FileName);
      goto ComponentDone;
    }
  }
  //
  // Prepend EFI_SOURCE to the component description file to get the
  // full path. Only do this if the path is not a full path already.
  //
  if (ComponentFilePathAbsolute == 0) {
    name = GetSymbolValue (EFI_SOURCE);
    sprintf (FileName, "%s\\%s", name, Line);
  } else {
    strcpy (FileName, Line);
  }
  //
  // Print a message, depending on verbose level.
  //
  if (DscSectionType == DSC_SECTION_TYPE_COMPONENTS) {
    Message (1, "Processing component         %s", FileName);
  } else {
    Message (1, "Processing library           %s", FileName);
  }
  //
  // Open the component's description file and get the sections. If we fail
  // to open it, see if they defined "OPTIONAL=1, in which case we'll just
  // ignore the component.
  //
  TempFptr = fopen (FileName, "r");
  if (TempFptr == NULL) {
    //
    // Better have defined OPTIONAL
    //
    if (GetSymbolValue (OPTIONAL_COMPONENT) != NULL) {
      if (atoi (GetSymbolValue (OPTIONAL_COMPONENT)) != 0) {
        Message (0, "Optional component '%s' not found", FileName);
        goto ComponentDone;
      }
    }

    ParserError (0, FileName, "failed to open component file");
    return STATUS_ERROR;
  } else {
    fclose (TempFptr);
  }

  DSCFileInit (&ComponentFile);
  ComponentCreated = 1;
  if (DSCFileSetFile (&ComponentFile, FileName)) {
    Error (NULL, 0, 0, NULL, "failed to preprocess component file '%s'", FileName);
    return STATUS_ERROR;
  }
  //
  // Add a symbol for the INF filename so users can create dependencies
  // in makefiles.
  //
  AddSymbol (INF_FILENAME, FileName, SYM_OVERWRITE | SYM_LOCAL | SYM_FILENAME);
  //
  // Process the [defines], [defines.$(PROCESSOR)], and [defines.$(PROCESSOR).$(PLATFORM)]
  // sections in the INF file
  //
  ProcessINFDefinesSection (&ComponentFile);
  //
  // Better have defined FILE_GUID if not a library
  //
  if ((GetSymbolValue (GUID) == NULL) &&
      (GetSymbolValue (FILE_GUID) == NULL) &&
      (DscSectionType == DSC_SECTION_TYPE_COMPONENTS)
      ) {
    Error (GetSymbolValue (INF_FILENAME), 1, 0, NULL, "missing FILE_GUID definition in component file");
    DSCFileDestroy (&ComponentFile);
    return STATUS_ERROR;
  }
  //
  // Better have defined base name
  //
  if (GetSymbolValue (BASE_NAME) == NULL) {
    Error (GetSymbolValue (INF_FILENAME), 1, 0, NULL, "missing BASE_NAME definition in INF file");
    DSCFileDestroy (&ComponentFile);
    return STATUS_ERROR;
  }
  //
  // Better have defined COMPONENT_TYPE, since it's used to find named sections.
  //
  if (GetSymbolValue (COMPONENT_TYPE) == NULL) {
    Error (GetSymbolValue (INF_FILENAME), 1, 0, NULL, "missing COMPONENT_TYPE definition in INF file");
    DSCFileDestroy (&ComponentFile);
    return STATUS_ERROR;
  }

  //
  // Create the source directory path from the component file's path. If the component
  // file's path is absolute, we may have problems here. Try to account for it though.
  //
  if (ComponentFilePathAbsolute == 0) {
    sprintf (
      FileName,
      "%s\\%s",
      GetSymbolValue (EFI_SOURCE),
      ComponentFilePath
      );
  } else {
    strcpy (FileName, ComponentFilePath);
  }
  AddSymbol (SOURCE_DIR, FileName, SYM_OVERWRITE | SYM_LOCAL | SYM_FILEPATH);

  //
  // Create the destination path. 
  // They may have defined DEST_DIR on the component INF line, so it's already
  // been defined, If that's the case, then don't set it to the path of this file.
  //
  TempCptr = GetSymbolValue (DEST_DIR);
  if (TempCptr == NULL) {
    if (ComponentFilePathAbsolute == 0) {
      //
      // The destination path is $(BUILD_DIR)\$(PROCESSOR)\component_path
      //
      sprintf (
        FileName,
        "%s\\%s\\%s",
        GetSymbolValue (BUILD_DIR),
        Processor,
        ComponentFilePath
        );
    } else {
      //
      // The destination path is $(BUILD_DIR)\$(PROCESSOR)\$(BASE_NAME)
      //
      sprintf (
        FileName,
        "%s\\%s\\%s",
        GetSymbolValue (BUILD_DIR),
        Processor,
        GetSymbolValue (BASE_NAME)
        );
    }
    AddSymbol (DEST_DIR, FileName, SYM_OVERWRITE | SYM_LOCAL | SYM_FILEPATH);
  } else {
    ReplaceSlash (TempCptr);
  }
  
  //
  // Create the output directory, then open the output component's makefile
  // we're going to create. Allow them to override the makefile name.
  //
  TempCptr = GetSymbolValue (MAKEFILE_NAME);
  if (TempCptr != NULL) {
    ExpandSymbols (TempCptr, ComponentMakefileName, sizeof (ComponentMakefileName), EXPANDMODE_NO_UNDEFS);
    TempCptr = ComponentMakefileName;
  } else {
    TempCptr = "makefile";
  }

  sprintf (FileName, "%s\\%s", GetSymbolValue (DEST_DIR), TempCptr);
  //
  // Save it now with path info
  //
  AddSymbol (MAKEFILE_NAME, FileName, SYM_OVERWRITE | SYM_LOCAL | SYM_FILENAME);

  if (MakeFilePath (FileName)) {
    return STATUS_ERROR;
  }

  if ((MakeFptr = fopen (FileName, "w")) == NULL) {
    Error (NULL, 0, 0, FileName, "could not create makefile");
    return STATUS_ERROR;
  }
  //
  // At this point we should have all the info we need to create a package
  // file if setup to do so. Libraries don't use package files, so
  // don't do this for libs.
  //
  if (DscSectionType == DSC_SECTION_TYPE_COMPONENTS) {
    CreatePackageFile (DSCFile);
  }

  //
  // Add a new build item to mCurrentBuildList
  //
  mCurrentBuildItem = AddBuildItem (mCurrentBuildList, GetSymbolValue (BASE_NAME), Processor, FileName);
  //
  // ProcessDsc allows duplicate base name libraries. Make sure the duplicate 
  // base name libraries will be built in the same order as listed in DSC file.
  //
  AddDependency (*mCurrentBuildList, mCurrentBuildItem, mCurrentBuildItem->BaseName, 1);

  //
  // Add Module name to the global module list
  //
  AddModuleName (&gGlobals.ModuleList, GetSymbolValue (BASE_NAME), GetSymbolValue (INF_FILENAME));
  //
  // Write an nmake line to makefile.out
  //
  fprintf (gGlobals.MakefileFptr, "  @cd %s\n", Processor);
  fprintf (gGlobals.MakefileFptr, "  $(MAKE) -f %s all\n", FileName);
  fprintf (gGlobals.MakefileFptr, "  @cd ..\n");

  //
  // Copy the common makefile section from the description file to
  // the component's makefile
  //
  WriteCommonMakefile (DSCFile, MakeFptr, Processor);
  //
  // Process the component's [nmake.common] and [nmake.$(PROCESSOR)] sections
  //
  ProcessINFNMakeSection (&ComponentFile, MakeFptr);
  //
  // Create the SOURCE_FILES macro that includes the names of all source
  // files in this component. This macro can then be used elsewhere to
  // process all the files making up the component. Required for scanning
  // files for string localization.
  // Also add source files to mCurrentBuildItem.
  //
  ProcessSourceFiles (DSCFile, &ComponentFile, MakeFptr, SOURCE_MODE_SOURCE_FILES);
  //
  // Create the include paths. Process [includes.common] and
  // [includes.$(PROCESSOR)] and [includes.$(PROCESSOR).$(PLATFORM)] sections.
  //
  ProcessIncludesSection (&ComponentFile, MakeFptr);
  //
  // Process all include source files to create a dependency list that can
  // be used in the makefile.
  //
  ProcessIncludeFiles (&ComponentFile, MakeFptr);
  //
  // Process the [sources.common], [sources.$(PROCESSOR)], and
  // [sources.$(PROCESSOR).$(PLATFORM)] files and emit their build commands
  //
  ProcessSourceFiles (DSCFile, &ComponentFile, MakeFptr, SOURCE_MODE_BUILD_COMMANDS);
  //
  // Process sources again to create an OBJECTS macro
  //
  ProcessObjects (&ComponentFile, MakeFptr);

  //
  // Add Single Module target : build and clean in top level makefile
  //
  fprintf (gGlobals.ModuleMakefileFptr, "%sbuild ::", GetSymbolValue (BASE_NAME));
  if (DscSectionType == DSC_SECTION_TYPE_COMPONENTS) {
    fprintf (gGlobals.ModuleMakefileFptr, " %sbuild", GLOBAL_LINK_LIB_NAME);
  }
  
  //
  // Process all the libraries to define "LIBS = x.lib y.lib..."
  // Be generous and append ".lib" if they forgot.
  // Make a macro definition: LIBS = $(LIBS) xlib.lib ylib.lib...
  // Add libs dependency for single module build: basenamebuild :: xlibbuild ylibbuild ...
  // Also add libs dependency to mCurrentBuildItem.
  //
  ProcessLibs (&ComponentFile, MakeFptr);

  fprintf (gGlobals.ModuleMakefileFptr, "\n");
  
  fprintf (gGlobals.ModuleMakefileFptr, "  @cd %s\n", Processor);
  fprintf (gGlobals.ModuleMakefileFptr, "  $(MAKE) -f %s all\n", FileName);
  fprintf (gGlobals.ModuleMakefileFptr, "  @cd ..\n\n");

  fprintf (gGlobals.ModuleMakefileFptr, "%sclean ::\n", GetSymbolValue (BASE_NAME));
  fprintf (gGlobals.ModuleMakefileFptr, "  $(MAKE) -f %s clean\n\n", FileName);
  
  //
  // Emit commands to create the component. These are simply copied from
  // the description file to the component's makefile. First look for
  // [build.$(PROCESSOR).$(BUILD_TYPE)]. If not found, then look for if
  // find a [build.$(PROCESSOR).$(COMPONENT_TYPE)] line.
  //
  Cptr = GetSymbolValue (BUILD_TYPE);
  if (Cptr != NULL) {
    sprintf (InLine, "build.%s.%s", Processor, Cptr);
    WriteComponentTypeBuildCommands (DSCFile, MakeFptr, InLine);
  } else {
    sprintf (InLine, "build.%s.%s", Processor, GetSymbolValue (COMPONENT_TYPE));
    WriteComponentTypeBuildCommands (DSCFile, MakeFptr, InLine);
  }
  //
  // Add it to the FV if not a library
  //
  if (DscSectionType == DSC_SECTION_TYPE_COMPONENTS) {
    //
    // Create the FV filename and add it to the FV.
    // By this point we know it's in FV.
    //
    Cptr = GetSymbolValue (FILE_GUID);
    if (Cptr == NULL) {
      Cptr = GetSymbolValue (GUID);
    }

    sprintf (InLine, "%s-%s", Cptr, GetSymbolValue (BASE_NAME));
    //
    // We've deprecated FV_EXT, which should be FFS_EXT, the extension
    // of the FFS file generated by GenFFSFile.
    //
    TempCptr = GetSymbolValue (FFS_EXT);
    if (TempCptr == NULL) {
      TempCptr = GetSymbolValue ("FV_EXT");
    }

    CFVAddFVFile (
      InLine,
      GetSymbolValue (COMPONENT_TYPE),
      GetSymbolValue (FV),
      Instance,
      TempCptr,
      Processor,
      GetSymbolValue (APRIORI),
      GetSymbolValue (BASE_NAME),
      Cptr
      );
  }
  //
  // Catch any failures and print the name of the component file
  // being processed to assist debugging.
  //
ComponentDone:

  Cptr = CatchException ();
  if (Cptr != NULL) {
    fprintf (stderr, "%s\n", Cptr);
    sprintf (InLine, "Processing of component %s failed", ArgLine);
    ThrowException (InLine);
  }

  if (MakeFptr != NULL) {
    fclose (MakeFptr);
  }

  if (ComponentCreated) {
    DSCFileDestroy (&ComponentFile);
  }

  return STATUS_SUCCESS;
}

static
int
CreatePackageFile (
  DSC_FILE          *DSCFile
  )
{
  INT8       *Package;
  SECTION    *TempSect;
  INT8       Str[MAX_LINE_LEN];
  INT8       StrExpanded[MAX_LINE_LEN];
  SMART_FILE *PkgFptr;
  int        Status;

  PkgFptr = NULL;

  //
  // First find out if PACKAGE_FILENAME or PACKAGE is defined. PACKAGE_FILENAME
  // is used to specify the exact package file to use. PACKAGE is used to
  // specify the package section name.
  //
  Package = GetSymbolValue (PACKAGE_FILENAME);
  if (Package != NULL) {
    //
    // Use existing file. We're done.
    //
    return STATUS_SUCCESS;
  }
  //
  // See if PACKAGE or PACKAGE_TAG is defined
  //
  Package = GetSymbolValue (PACKAGE);
  if (Package == NULL) {
    Package = GetSymbolValue (PACKAGE_TAG);
  }

  if (Package == NULL) {
    //
    // Not defined either. Assume they are not using the package functionality
    // of this utility. However define the PACKAGE_FILENAME macro to the
    // best-guess value.
    //
    sprintf (
      Str,
      "%s\\%s.pkg",
      GetSymbolValue (SOURCE_DIR),
      GetSymbolValue (BASE_NAME)
      );
 
    //
    // Expand symbols in the package filename
    //
    ExpandSymbols (Str, StrExpanded, sizeof (StrExpanded), EXPANDMODE_NO_UNDEFS);    
 
    AddSymbol (PACKAGE_FILENAME, StrExpanded, SYM_LOCAL | SYM_FILENAME);
    return STATUS_SUCCESS;
  }
  //
  // Save the position in the DSC file.
  // Find the [package.$(COMPONENT_TYPE).$(PACKAGE)] section in the DSC file
  //
  Status = STATUS_SUCCESS;
  DSCFileSavePosition (DSCFile);
  sprintf (Str, "%s.%s.%s", PACKAGE, GetSymbolValue (COMPONENT_TYPE), Package);
  TempSect = DSCFileFindSection (DSCFile, Str);
  if (TempSect != NULL) {
    //
    // So far so good. Create the name of the package file, then open it up
    // for writing. File name is c:\...\oem\platform\nt32\ia32\...\BaseName.pkg.
    //
    sprintf (
      Str,
      "%s\\%s.pkg",
      GetSymbolValue (DEST_DIR),
      GetSymbolValue (BASE_NAME)
      );
    
    //
    // Expand symbols in the package filename
    //
    ExpandSymbols (Str, StrExpanded, sizeof (StrExpanded), EXPANDMODE_NO_UNDEFS);    
    
    //
    // Try to open the file, then save the file name as the PACKAGE_FILENAME
    // symbol for use elsewhere.
    //
    if ((PkgFptr = SmartOpen (StrExpanded)) == NULL) {
      Error (NULL, 0, 0, Str, "could not open package file for writing");
      Status = STATUS_ERROR;
      goto Finish;
    }

    AddSymbol (PACKAGE_FILENAME, StrExpanded, SYM_LOCAL | SYM_FILENAME);
    //
    // Now read lines in from the DSC file and write them back out to the
    // package file (with string substitution).
    //
    while (DSCFileGetLine (DSCFile, Str, sizeof (Str)) != NULL) {
      //
      // Expand symbols, then write the line out to the package file
      //
      ExpandSymbols (Str, StrExpanded, sizeof (StrExpanded), EXPANDMODE_RECURSIVE);
      SmartWrite (PkgFptr, StrExpanded);
    }
  } else {
    Warning (
      NULL,
      0,
      0,
      NULL,
      "cannot locate package section [%s] in DSC file for %s",
      Str,
      GetSymbolValue (INF_FILENAME)
      );
    Status = STATUS_WARNING;
    goto Finish;
  }

  if (PkgFptr != NULL) {
    SmartClose (PkgFptr);
  }

Finish:
  //
  // Restore the position in the DSC file
  //
  DSCFileRestorePosition (DSCFile);

  return STATUS_SUCCESS;
}

static
int
ProcessINFDefinesSection (
  DSC_FILE   *ComponentFile
  )
/*++

Routine Description:

  Process the [defines.xxx] sections of the component description file. Process
  platform first, then processor. In this way, if a platform wants and override,
  that one gets parsed first, and later assignments do not overwrite the value.
  
Arguments:

  ComponentFile     - section info on the component file being processed

Returns:

 
--*/
{
  INT8  *Cptr;
  INT8  Str[MAX_LINE_LEN];

  //
  // Find a [defines.$(PROCESSOR).$(PLATFORM)] section and process it
  //
  Cptr = GetSymbolValue (PLATFORM);
  if (Cptr != NULL) {
    sprintf (
      Str,
      "%s.%s.%s",
      DEFINES_SECTION_NAME,
      GetSymbolValue (PROCESSOR),
      Cptr
      );
    ProcessINFDefinesSectionSingle (ComponentFile, Str);
  }
  //
  // Find a [defines.$(PROCESSOR)] section and process it
  //
  sprintf (Str, "%s.%s", DEFINES_SECTION_NAME, GetSymbolValue (PROCESSOR));
  ProcessINFDefinesSectionSingle (ComponentFile, Str);

  //
  // Find a [defines] section and process it
  //
  if (ProcessINFDefinesSectionSingle (ComponentFile, DEFINES_SECTION_NAME) != STATUS_SUCCESS) {
    Error (NULL, 0, 0, NULL, "missing [defines] section in component file %s", GetSymbolValue (INF_FILENAME));
    return STATUS_ERROR;
  }

  return STATUS_SUCCESS;
}

static
int
ProcessINFDefinesSectionSingle (
  DSC_FILE  *ComponentFile,
  INT8      *SectionName
  )
{
  INT8    *Cptr;
  INT8    Str[MAX_LINE_LEN];
  INT8    ExpandedLine[MAX_LINE_LEN];
  SECTION *TempSect;

  TempSect = DSCFileFindSection (ComponentFile, SectionName);
  if (TempSect != NULL) {
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      ExpandSymbols (Str, ExpandedLine, sizeof (ExpandedLine), 0);
      Cptr = StripLine (ExpandedLine);
      //
      // Don't process blank lines.
      //
      if (*Cptr) {
        //
        // Add without overwriting macros specified on the component line
        // in the description file
        //
        AddSymbol (Cptr, NULL, SYM_LOCAL);
      }
    }
  } else {
    return STATUS_WARNING;
  }

  return STATUS_SUCCESS;
}

static
int
ProcessINFNMakeSection (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  )
/*++

Routine Description:

  Process the [nmake.common] and [nmake.$(PROCESSOR)] sections of the component
  description file and write and copy them to the component's makefile.
  
Arguments:

  ComponentFile     - section info on the component file being processed
  MakeFptr          - file pointer to the component' makefile we're creating

Returns:

  Always STATUS_SUCCESS right now, since the sections are optional.
  
--*/
{
  INT8    *Cptr;
  INT8    Str[MAX_LINE_LEN];
  INT8    ExpandedLine[MAX_LINE_LEN];
  SECTION *TempSect;

  //
  // Copy the [nmake.common] and [nmake.$(PROCESSOR)] sections from the
  // component file directly to the output file.
  // The line will be stripped and don't print blank lines
  //
  sprintf (Str, "%s.%s", NMAKE_SECTION_NAME, COMMON_SECTION_NAME);
  TempSect = DSCFileFindSection (ComponentFile, Str);
  if (TempSect != NULL) {
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      ExpandSymbols (
        Str,
        ExpandedLine,
        sizeof (ExpandedLine),
        EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
        );
      Cptr = StripLine (ExpandedLine);
      if (*Cptr) {
        fprintf (MakeFptr, "%s\n", Cptr);
      }
    }

    fprintf (MakeFptr, "\n");
  } else {
    Error (GetSymbolValue (INF_FILENAME), 1, 0, Str, "section not found in component INF file");
  }

  sprintf (Str, "%s.%s", NMAKE_SECTION_NAME, GetSymbolValue (PROCESSOR));
  TempSect = DSCFileFindSection (ComponentFile, Str);
  if (TempSect != NULL) {
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      ExpandSymbols (
        Str,
        ExpandedLine,
        sizeof (ExpandedLine),
        EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
        );
      Cptr = StripLine (ExpandedLine);
      if (*Cptr) {
        fprintf (MakeFptr, "%s\n", Cptr);
      }
    }

    fprintf (MakeFptr, "\n");
  }
  //
  // Do the same for [nmake.$(PROCESSOR).$(PLATFORM)]
  //
  Cptr = GetSymbolValue (PLATFORM);
  if (Cptr != NULL) {
    sprintf (Str, "%s.%s.%s", NMAKE_SECTION_NAME, GetSymbolValue (PROCESSOR), Cptr);
    TempSect = DSCFileFindSection (ComponentFile, Str);
    if (TempSect != NULL) {
      while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
        ExpandSymbols (
          Str,
          ExpandedLine,
          sizeof (ExpandedLine),
          EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
          );
        Cptr = StripLine (ExpandedLine);
        if (*Cptr) {
          fprintf (MakeFptr, "%s\n", Cptr);
        }
      }

      fprintf (MakeFptr, "\n");
    }
  }

  return STATUS_SUCCESS;
}

static
int
ProcessIncludesSection (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  )
/*++

Routine Description:

  Process the [includes.common], [includes.processor], and 
  [includes.processor.platform] section of the component description file 
  and write the appropriate macros to the component's makefile.

  Process in reverse order to allow overrides on platform basis.
    
Arguments:

  ComponentFile     - section info on the component file being processed
  MakeFptr          - file pointer to the component' makefile we're creating

Returns:

  Always STATUS_SUCCESS right now, since the sections are optional.
  
--*/
{
  INT8  *Cptr;
  INT8  Str[MAX_LINE_LEN];
  INT8  *Processor;
  INT8  *OverridePath;

  //
  // Write a useful comment to the output makefile so the user knows where
  // the data came from.
  //
  fprintf (MakeFptr, "#\n# Tool-generated list of include paths that are created\n");
  fprintf (MakeFptr, "# from the list of include paths in the [includes.*] sections\n");
  fprintf (MakeFptr, "# of the component INF file.\n#\n");

  //
  // We use this a lot here, so get the value only once.
  //
  Processor = GetSymbolValue (PROCESSOR);
  //
  // If they're using an override source path, then add OverridePath and
  // OverridePath\$(PROCESSOR) to the list of include paths.
  //
  OverridePath = GetSymbolValue (SOURCE_OVERRIDE_PATH);
  if (OverridePath != NULL) {
    ReplaceSlash (OverridePath);
    fprintf (MakeFptr, "!IF EXIST(%s)\n", OverridePath);
    fprintf (MakeFptr, "INC = $(INC) -I %s\n", OverridePath);
    fprintf (MakeFptr, "!IF EXIST(%s\\%s)\n", OverridePath, Processor);
    fprintf (MakeFptr, "INC = $(INC) -I %s\\%s\n", OverridePath, Processor);
    fprintf (MakeFptr, "!ENDIF\n");
    fprintf (MakeFptr, "!ELSE\n");
    fprintf (MakeFptr, "!MESSAGE Warning: include dir %s does not exist\n", OverridePath);
    fprintf (MakeFptr, "!ENDIF\n");
  }
  //
  // Try for an [includes.$(PROCESSOR).$(PLATFORM)]
  //
  Cptr = GetSymbolValue (PLATFORM);
  if (Cptr != NULL) {
    sprintf (Str, "%s.%s.%s", INCLUDE_SECTION_NAME, Processor, Cptr);
    ProcessIncludesSectionSingle (ComponentFile, MakeFptr, Str);
  }
  //
  // Now the [includes.$(PROCESSOR)] section
  //
  sprintf (Str, "%s.%s", INCLUDE_SECTION_NAME, Processor);
  ProcessIncludesSectionSingle (ComponentFile, MakeFptr, Str);

  //
  // Now the [includes.common] section
  //
  sprintf (Str, "%s.%s", INCLUDE_SECTION_NAME, COMMON_SECTION_NAME);
  ProcessIncludesSectionSingle (ComponentFile, MakeFptr, Str);

  //
  // Done
  //
  fprintf (MakeFptr, "\n");
  return STATUS_SUCCESS;
}
//
// Process one of the [includes.xxx] sections to create a list of all
// the include paths.
//
static
int
ProcessIncludesSectionSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  )
{
  INT8    *Cptr;
  SECTION *TempSect;
  INT8    Str[MAX_LINE_LEN];
  INT8    ExpandedLine[MAX_LINE_LEN];
  INT8    *Processor;

  TempSect = DSCFileFindSection (ComponentFile, SectionName);
  if (TempSect != NULL) {
    //
    // Add processor subdirectory on every include path
    //
    Processor = GetSymbolValue (PROCESSOR);
    //
    // Copy lines directly
    //
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      ExpandSymbols (Str, ExpandedLine, sizeof (ExpandedLine), 0);
      Cptr = StripLine (ExpandedLine);
      //
      // Don't process blank lines
      //
      if (*Cptr) {
        ReplaceSlash (Cptr);
        //
        // Strip off trailing slash
        //
        if (Cptr[strlen (Cptr) - 1] == '\\') {
          Cptr[strlen (Cptr) - 1] = 0;
        }
        //
        // Special case of ".". Replace it with source path
        // and the rest of the line (for .\$(PROCESSOR))
        //
        if (*Cptr == '.') {
          //
          // Handle case of just a "."
          //
          if (Cptr[1] == 0) {
            fprintf (MakeFptr, "INC = $(INC) -I $(SOURCE_DIR)\n");
            fprintf (MakeFptr, "!IF EXIST($(SOURCE_DIR)\\%s)\n", Processor);
            fprintf (MakeFptr, "INC = $(INC) -I $(SOURCE_DIR)\\%s\n", Processor);
            fprintf (MakeFptr, "!ENDIF\n");
          } else {
            //
            // Handle case of ".\path\path\path" or "..\path\path\path"
            //
            fprintf (MakeFptr, "!IF EXIST($(SOURCE_DIR)\\%s)\n", Cptr);
            fprintf (MakeFptr, "INC = $(INC) -I $(SOURCE_DIR)\\%s\n", Cptr);
            fprintf (MakeFptr, "!IF EXIST($(SOURCE_DIR)\\%s\\%s)\n", Cptr, Processor);
            fprintf (MakeFptr, "INC = $(INC) -I $(SOURCE_DIR)\\%s\\%s\n", Cptr, Processor);
            fprintf (MakeFptr, "!ENDIF\n");
            fprintf (MakeFptr, "!ELSE\n");
            fprintf (MakeFptr, "!MESSAGE Warning: include dir $(SOURCE_DIR)\\%s does not exist\n", Cptr);
            fprintf (MakeFptr, "!ENDIF\n");
          }
        } else if ((Cptr[1] != ':') && isalpha (*Cptr)) {
          fprintf (MakeFptr, "!IF EXIST($(EFI_SOURCE)\\%s)\n", Cptr);
          fprintf (MakeFptr, "INC = $(INC) -I $(EFI_SOURCE)\\%s\n", Cptr);
          fprintf (MakeFptr, "!IF EXIST($(EFI_SOURCE)\\%s\\%s)\n", Cptr, Processor);
          fprintf (MakeFptr, "INC = $(INC) -I $(EFI_SOURCE)\\%s\\%s\n", Cptr, Processor);
          fprintf (MakeFptr, "!ENDIF\n");
          fprintf (MakeFptr, "!ELSE\n");
          fprintf (MakeFptr, "!MESSAGE Warning: include dir $(EFI_SOURCE)\\%s does not exist\n", Cptr);
          fprintf (MakeFptr, "!ENDIF\n");
        } else {
          //
          // The line is something like: $(EFI_SOURCE)\dxe\include. Add it to
          // the existing $(INC) definition. Add user includes before any
          // other existing paths.
          //
          fprintf (MakeFptr, "!IF EXIST(%s)\n", Cptr);
          fprintf (MakeFptr, "INC = $(INC) -I %s\n", Cptr);
          fprintf (MakeFptr, "!IF EXIST(%s\\%s)\n", Cptr, Processor);
          fprintf (MakeFptr, "INC = $(INC) -I %s\\%s\n", Cptr, Processor);
          fprintf (MakeFptr, "!ENDIF\n");
          fprintf (MakeFptr, "!ELSE\n");
          fprintf (MakeFptr, "!MESSAGE Warning: include dir %s does not exist\n", Cptr);
          fprintf (MakeFptr, "!ENDIF\n");
        }
      }
    }
  }

  return STATUS_SUCCESS;
}

static
int
ProcessSourceFiles (
  DSC_FILE  *DSCFile,
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  UINT32    Mode
  )
/*++

Routine Description:

  Process the [sources.common], [sources.$(PROCESSOR)], and 
  [sources.$(PROCESSOR).$(PLATFORM] sections of the component
  description file and write the appropriate build commands out to the 
  component's makefile. If $(SOURCE_SELECT) is defined, then it overrides
  the source selections. We use this functionality for SMM.
  
Arguments:

  ComponentFile     - section info on the component file being processed
  MakeFptr          - file pointer to the component' makefile we're creating
  DSCFile           - section info on the description file we're processing
  Mode              - to write build commands, or just create a list
                      of sources.

Returns:

  Always STATUS_SUCCESS right now, since the sections are optional.
  
--*/
{
  INT8  Str[MAX_LINE_LEN];
  INT8  *Processor;
  INT8  *Platform;
  INT8  *SourceSelect;
  INT8  *CStart;
  INT8  *CEnd;
  INT8  CSave;
  INT8  *CopySourceSelect;

  if (Mode & SOURCE_MODE_SOURCE_FILES) {
    //
    // Write a useful comment to the output makefile so the user knows where
    // the data came from.
    //
    fprintf (MakeFptr, "#\n# Tool-generated list of source files that are created\n");
    fprintf (MakeFptr, "# from the list of source files in the [sources.*] sections\n");
    fprintf (MakeFptr, "# of the component INF file.\n#\n");
  }
  
  //
  // We use this a lot here, so get the value only once.
  //
  Processor = GetSymbolValue (PROCESSOR);
  //
  // See if they defined SOURCE_SELECT=xxx,yyy in which case we'll
  // select each [sources.xxx] and [sources.yyy] files and process
  // them.
  //
  SourceSelect = GetSymbolValue (SOURCE_SELECT);

  if (SourceSelect != NULL) {
    //
    // Make a copy of the string and break it up (comma-separated) and
    // select each [sources.*] file from the INF.
    //
    CopySourceSelect = (INT8 *) malloc (strlen (SourceSelect) + 1);
    if (CopySourceSelect == NULL) {
      Error (NULL, 0, 0, NULL, "failed to allocate memory");
      return STATUS_ERROR;
    }

    strcpy (CopySourceSelect, SourceSelect);
    CStart  = CopySourceSelect;
    CEnd    = CStart;
    while (*CStart) {
      CEnd = CStart + 1;
      while (*CEnd && *CEnd != ',') {
        CEnd++;
      }

      CSave = *CEnd;
      *CEnd = 0;
      sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, CStart);
      ProcessSourceFilesSection (DSCFile, ComponentFile, MakeFptr, Str, Mode);
      //
      // Restore the terminator and advance
      //
      *CEnd   = CSave;
      CStart  = CEnd;
      if (*CStart) {
        CStart++;
      }
    }

    free (CopySourceSelect);

  } else {
    //
    // Process all the [sources.common] source files to make them build
    //
    sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, COMMON_SECTION_NAME);
    ProcessSourceFilesSection (DSCFile, ComponentFile, MakeFptr, Str, Mode);
    //
    // Now process the [sources.$(PROCESSOR)] files.
    //
    sprintf (Str, "sources.%s", Processor);
    ProcessSourceFilesSection (DSCFile, ComponentFile, MakeFptr, Str, Mode);
    //
    // Now process the [sources.$(PROCESSOR).$(PLATFORM)] files.
    //
    Platform = GetSymbolValue (PLATFORM);
    if (Platform != NULL) {
      sprintf (Str, "sources.%s.%s", Processor, Platform);
      ProcessSourceFilesSection (DSCFile, ComponentFile, MakeFptr, Str, Mode);
    }
  }

  fprintf (MakeFptr, "\n");
  return STATUS_SUCCESS;
}

/*++

Routine Description:
  Given a source file line from an INF file, parse it to see if there are
  any defines on it. If so, then add them to the symbol table.
  Also, terminate the line after the file name.
  
Arguments:
  SourceFileLine - a line from a [sources.?] section of the INF file. Likely
  something like:
  
  MySourceFile.c   BUILT_NAME=$(BUILD_DIR)\MySourceFile.obj

Returns:
  Nothing.
  
--*/
static
void
AddFileSymbols (
  INT8    *SourceFileLine
  )
{
  int Len;
  //
  // Skip spaces
  //
  for (; *SourceFileLine && isspace (*SourceFileLine); SourceFileLine++)
    ;
  for (; *SourceFileLine && !isspace (*SourceFileLine); SourceFileLine++)
    ;
  if (*SourceFileLine) {
    *SourceFileLine = 0;
    SourceFileLine++;
    //
    // AddSymbol() will parse it for us, and return the length. Keep calling
    // it until it reports an error or is done.
    //
    do {
      Len = AddSymbol (SourceFileLine, NULL, SYM_FILE);
      SourceFileLine += Len;
    } while (Len > 0);
  }
}
//
// Process a single section of source files in the component INF file
//
static
int
ProcessSourceFilesSection (
  DSC_FILE  *DSCFile,
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName,
  UINT32    Mode
  )
{
  INT8    *Cptr;
  INT8    FileName[MAX_EXP_LINE_LEN];
  INT8    FilePath[MAX_PATH];
  INT8    TempFileName[MAX_PATH];
  SECTION *TempSect;
  INT8    Str[MAX_LINE_LEN];
  INT8    *Processor;
  INT8    *OverridePath;
  FILE    *FPtr;

  TempSect = DSCFileFindSection (ComponentFile, SectionName);
  if (TempSect != NULL) {
    Processor = GetSymbolValue (PROCESSOR);
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      Cptr = StripLine (Str);
      //
      // Don't process blank lines
      //
      if (*Cptr) {
        //
        // Expand symbols in the filename, then parse the line for symbol
        // definitions. AddFileSymbols() will null-terminate the line
        // after the file name. Save a copy for override purposes, in which
        // case we'll need to know the file name and path (in case it's in
        // a subdirectory).
        //
        ExpandSymbols (Cptr, FileName, sizeof (FileName), 0);
        AddFileSymbols (FileName);
        ReplaceSlash (FileName);
        //
        // Set the SOURCE_FILE_NAME symbol. What we have now is the name of
        // the file, relative to the location of the INF file. So prepend
        // $(SOURCE_DIR) to it first.
        //
        if (IsAbsolutePath (FileName)) {
          strcpy (TempFileName, FileName);
        } else {
          strcpy (TempFileName, "$(SOURCE_DIR)\\");
          strcat (TempFileName, FileName);
        }
        AddSymbol (SOURCE_FILE_NAME, TempFileName, SYM_FILE | SYM_OVERWRITE);
        //
        // Extract path information from the source file and set internal
        // variable SOURCE_RELATIVE_PATH. Only do this if the path
        // contains a backslash.
        //
        strcpy (FilePath, FileName);
        for (Cptr = FilePath + strlen (FilePath) - 1; (Cptr > FilePath) && (*Cptr != '\\'); Cptr--)
          ;
        if (*Cptr == '\\') {
          *(Cptr + 1) = 0;
          AddSymbol (SOURCE_RELATIVE_PATH, FilePath, SYM_FILE);
        }
        //
        // Define another internal symbol for the name of the file without
        // the path and extension.
        //
        for (Cptr = FileName + strlen (FileName) - 1; (Cptr > FileName) && (*Cptr != '\\'); Cptr--)
          ;
        if (*Cptr == '\\') {
          Cptr++;
        }

        strcpy (FilePath, Cptr);
        //
        // We now have a file name with no path information. Before we do anything else,
        // see if OVERRIDE_PATH is set, and if so, see if file $(OVERRIDE_PATH)FileName
        // exists. If it does, then recursive call this function to use the override file
        // instead of the one from the INF file.
        //
        if (IsAbsolutePath (FileName)) {
          OverridePath = NULL;
        } else {
          OverridePath = GetSymbolValue (SOURCE_OVERRIDE_PATH);
        }
        if (OverridePath != NULL) {
          ReplaceSlash (OverridePath);
          //
          // See if the file exists. If it does, reset the SOURCE_FILE_NAME symbol.
          //
          strcpy (TempFileName, OverridePath);
          strcat (TempFileName, "\\");
          strcat (TempFileName, FileName);
          if ((FPtr = fopen (TempFileName, "rb")) != NULL) {
            fclose (FPtr);
            AddSymbol (SOURCE_FILE_NAME, TempFileName, SYM_FILE | SYM_OVERWRITE);
            //
            // Print a message. This function is called to create build commands
            // for source files, and to create a macro of all source files. Therefore
            // do this check so we don't print the override message multiple times.
            //
            if (Mode & SOURCE_MODE_BUILD_COMMANDS) {
              fprintf (stdout, "Override: %s\n", TempFileName);
            }
          } else {
            //
            // Set override path to null to use as a flag below
            //
            OverridePath = NULL;
          }
        }

        //
        // Start at the end and work back
        //
        for (Cptr = FilePath + strlen (FilePath) - 1; (Cptr > FilePath) && (*Cptr != '\\') && (*Cptr != '.'); Cptr--)
          ;
        if (*Cptr == '.') {
          *Cptr = 0;
          AddSymbol (SOURCE_FILE_EXTENSION, Cptr + 1, SYM_FILE);
        }

        AddSymbol (SOURCE_BASE_NAME, FilePath, SYM_FILE);
        //
        // If we're just creating the SOURCE_FILES macro, then write the
        // file name out to the makefile.
        //
        if (Mode & SOURCE_MODE_SOURCE_FILES) {
          //
          // If we're processing an override file, then use the file name as-is
          //
          if (OverridePath != NULL) {
            //
            // SOURCE_FILES = $(SOURCE_FILES) c:\Path\ThisFile.c
            //
            fprintf (MakeFptr, "SOURCE_FILES = $(SOURCE_FILES) %s\n", TempFileName);
            //
            // Save the source absolute path
            //
            if (PathCanonicalize (FilePath, TempFileName)) {
              AddSourceFile (mCurrentBuildItem, FilePath);
            }
          } else if (IsAbsolutePath (FileName)) {
            //
            // For Absolute path, don't print $(SOURCE_FILE) directory.
            //
            fprintf (MakeFptr, "SOURCE_FILES = $(SOURCE_FILES) %s\n", FileName);
            //
            // Save the source absolute path
            //
            if (PathCanonicalize (FilePath, FileName)) {
              AddSourceFile (mCurrentBuildItem, FilePath);
            }
          } else {
            //
            // SOURCE_FILES = $(SOURCE_FILES) $(SOURCE_DIR)\ThisFile.c
            //
            fprintf (MakeFptr, "SOURCE_FILES = $(SOURCE_FILES) $(SOURCE_DIR)\\%s\n", FileName);
            //
            // Save the source absolute path
            //
            sprintf (Str, "%s\\%s", GetSymbolValue (SOURCE_DIR), FileName);
            if (PathCanonicalize (FilePath, Str)) {
              AddSourceFile (mCurrentBuildItem, FilePath);
            }
          }
        } else if (Mode & SOURCE_MODE_BUILD_COMMANDS) {
          //
          // Write the build commands for this file per the build commands
          // for this file type as defined in the description file.
          // Also create the directory for it in the build path.
          //
          WriteCompileCommands (DSCFile, MakeFptr, FileName, Processor);
          if (!IsAbsolutePath (FileName)) {
            sprintf (Str, "%s\\%s", GetSymbolValue (DEST_DIR), FileName);
            MakeFilePath (Str);
            //
            // Get all output directory for build output files.
            //
            Cptr = FileName + strlen (FileName) - 1;
            for (; (Cptr > FileName) && (*Cptr != '\\'); Cptr--);
            if (*Cptr == '\\') {
              *Cptr = '\0';
              AddModuleName (&gGlobals.OutdirList, FileName, NULL);
            }
          }
        }
        //
        // Remove file-level symbols
        //
        RemoveFileSymbols ();
      }
    }
  }

  return STATUS_SUCCESS;
}
//
// Process the INF [sources.*] sections and emit the OBJECTS = .....
// lines to the component's makefile.
//
static
int
ProcessObjects (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  )
{
  INT8  Str[MAX_LINE_LEN];
  INT8  *Processor;
  INT8  *Platform;
  INT8  *SourceSelect;
  INT8  *CStart;
  INT8  *CEnd;
  INT8  CSave;
  INT8  *CopySourceSelect;
  SYMBOL *TempSymbol;

  //
  // Write a useful comment to the output makefile so the user knows where
  // the data came from.
  //
  fprintf (MakeFptr, "#\n# Tool-generated list of object files that are created\n");
  fprintf (MakeFptr, "# from the list of source files in the [sources.*] sections\n");
  fprintf (MakeFptr, "# of the component INF file.\n#\n");
  //
  // We use this a lot here, so get the value only once.
  //
  Processor = GetSymbolValue (PROCESSOR);
  //
  // Now define the OBJECTS variable and assign it to be all the object files we're going
  // to create. Afterwards create a pseudo-target objects to let the user quickly just compile
  // the source files. This means we need to process all the common objects and
  // processor-specific objects again.
  //
  fprintf (MakeFptr, "OBJECTS = $(OBJECTS) ");
  //
  // See if they defined SOURCE_SELECT=xxx,yyy in which case well
  // select each [sources.xxx] and [sources.yyy] files and process
  // them.
  //
  SourceSelect = GetSymbolValue (SOURCE_SELECT);

  if (SourceSelect != NULL) {
    //
    // Make a copy of the string and break it up (comma-separated) and
    // select each [sources.*] file from the INF.
    //
    CopySourceSelect = (INT8 *) malloc (strlen (SourceSelect) + 1);
    if (CopySourceSelect == NULL) {
      Error (NULL, 0, 0, NULL, "failed to allocate memory");
      return STATUS_ERROR;
    }

    strcpy (CopySourceSelect, SourceSelect);
    CStart  = CopySourceSelect;
    CEnd    = CStart;
    while (*CStart) {
      CEnd = CStart + 1;
      while (*CEnd && *CEnd != ',') {
        CEnd++;
      }

      CSave = *CEnd;
      *CEnd = 0;
      sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, CStart);
      ProcessObjectsSingle (ComponentFile, MakeFptr, Str);
      //
      // Restore the terminator and advance
      //
      *CEnd   = CSave;
      CStart  = CEnd;
      if (*CStart) {
        CStart++;
      }
    }

    free (CopySourceSelect);
  
  } else {
    //
    // Now process all the [sources.common] files and emit build commands for them
    //
    sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, COMMON_SECTION_NAME);
    if (ProcessObjectsSingle (ComponentFile, MakeFptr, Str) != STATUS_SUCCESS) {
      Warning (GetSymbolValue (INF_FILENAME), 1, 0, NULL, "no [%s] section found in component description", Str);
    }
    //
    // Now process any processor-specific source files in [sources.$(PROCESSOR)]
    //
    sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, Processor);
    ProcessObjectsSingle (ComponentFile, MakeFptr, Str);

    //
    // Now process any [sources.$(PROCESSOR).$(PLATFORM)] files
    //
    Platform = GetSymbolValue (PLATFORM);
    if (Platform != NULL) {
      sprintf (Str, "sources.%s.%s", Processor, Platform);
      ProcessObjectsSingle (ComponentFile, MakeFptr, Str);
    }
  }

  fprintf (MakeFptr, "\n\n");

  //
  // Write a useful comment to the output makefile so the user knows where
  // the data came from.
  //
  fprintf (MakeFptr, "#\n# Tool-generated list of dest output dirs that are created\n");
  fprintf (MakeFptr, "# from the list of source files in the [sources.*] sections\n");
  fprintf (MakeFptr, "# of the component INF file.\n#\n");
  //
  // Create output directory list 
  // for clean target to delete all build output files.
  //
  fprintf (MakeFptr, "DEST_OUTPUT_DIRS = $(%s) ", DEST_DIR);

  TempSymbol = gGlobals.OutdirList;
  while (TempSymbol != NULL) {
    fprintf (MakeFptr, "\\\n                   $(%s)\\%s   ", 
             DEST_DIR, TempSymbol->Name);
    TempSymbol = TempSymbol->Next;
  }
  fprintf (MakeFptr, "\n\n");
  
  //
  // clean up for the next module
  //
  FreeSymbols (gGlobals.OutdirList);
  gGlobals.OutdirList = NULL;

  return STATUS_SUCCESS;
}

static
INT8 *
BuiltFileExtension (
  INT8      *SourceFileName
  )
{
  int   i;
  INT8  *Cptr;
  //
  // Find the dot in the filename extension
  //
  for (Cptr = SourceFileName + strlen (SourceFileName) - 1;
       (Cptr > SourceFileName) && (*Cptr != '\\') && (*Cptr != '.');
       Cptr--
      ) {
    //
    // Do nothing
    //
  }

  if (*Cptr != '.') {
    return NULL;
  }
  //
  // Look through our list of known file types and return a pointer to
  // its built file extension.
  //
  for (i = 0; mFileTypes[i].Extension != NULL; i++) {
    if (_stricmp (Cptr, mFileTypes[i].Extension) == 0) {
      return mFileTypes[i].BuiltExtension;
    }
  }

  return NULL;
}

int
ProcessObjectsSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  )
{
  INT8    *Cptr;
  INT8    *Cptr2;
  INT8    Str[MAX_LINE_LEN];
  INT8    FileName[MAX_EXP_LINE_LEN];
  SECTION *TempSect;

  TempSect = DSCFileFindSection (ComponentFile, SectionName);
  if (TempSect != NULL) {
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      Cptr = StripLine (Str);
      //
      // Don't process blank lines
      //
      if (*Cptr) {
        //
        // Expand symbols then create the output filename. We'll do a lookup
        // on the source file's extension to determine what the extension of
        // the built version of the file is. For example, .c -> .obj.
        //
        if (!IsIncludeFile (Cptr)) {
          ExpandSymbols (Cptr, FileName, sizeof (FileName), 0);
          ReplaceSlash (FileName);
          Cptr2 = BuiltFileExtension (FileName);
          if (Cptr2 != NULL) {
            SetFileExtension (FileName, Cptr2);
            if (!IsAbsolutePath (FileName)) {
              fprintf (MakeFptr, "\\\n          $(%s)\\%s   ", DEST_DIR, FileName);
            } else {
              fprintf (MakeFptr, "\\\n          %s   ", FileName);
            }
          }
        }
      }
    }
  } else {
    return STATUS_WARNING;
  }

  return STATUS_SUCCESS;
}
//
// Process all [libraries.*] sections in the component INF file to create a
// macro to the component's output makefile: LIBS = Lib1 Lib2, ...
//
static
int
ProcessLibs (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr
  )
{
  INT8  Str[MAX_LINE_LEN];
  INT8  *Processor;
  INT8  *Platform;

  //
  // Print a useful comment to the component's makefile so the user knows
  // where the data came from.
  //
  fprintf (MakeFptr, "#\n# Tool-generated list of libraries that are generated\n");
  fprintf (MakeFptr, "# from the list of libraries listed in the [libraries.*] sections\n");
  fprintf (MakeFptr, "# of the component INF file.\n#\n");
  
  fprintf (MakeFptr, "LIBS = $(LIBS) ");
  
  Processor = GetSymbolValue (PROCESSOR);
  //
  // Process [libraries.common] files
  //
  sprintf (Str, "%s.%s", LIBRARIES_SECTION_NAME, COMMON_SECTION_NAME);
  ProcessLibsSingle (ComponentFile, MakeFptr, Str);
  //
  // Process the [libraries.$(PROCESSOR)] libraries to define "LIBS = x.lib y.lib..."
  //
  sprintf (Str, "%s.%s", LIBRARIES_SECTION_NAME, Processor);
  ProcessLibsSingle (ComponentFile, MakeFptr, Str);
  //
  // Now process any [libraries.$(PROCESSOR).$(PLATFORM)] files
  //
  Platform = GetSymbolValue (PLATFORM);
  if (Platform != NULL) {
    sprintf (Str, "%s.%s.%s", LIBRARIES_SECTION_NAME, Processor, Platform);
    ProcessLibsSingle (ComponentFile, MakeFptr, Str);
  }
  //
  // Process any [libraries.platform] files
  //
  ProcessLibsSingle (ComponentFile, MakeFptr, LIBRARIES_PLATFORM_SECTION_NAME);

  fprintf (MakeFptr, "\n\n");
  return STATUS_SUCCESS;
}

static
int
ProcessLibsSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  )
{
  INT8    *Cptr;
  INT8    Str[MAX_LINE_LEN];
  INT8    ExpandedLine[MAX_LINE_LEN];
  SECTION *TempSect;

  TempSect = DSCFileFindSection (ComponentFile, SectionName);
  if (TempSect != NULL) {
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      ExpandSymbols (Str, ExpandedLine, sizeof (ExpandedLine), 0);
      Cptr = StripLine (ExpandedLine);
      //
      // Don't process blank lines
      //
      if (*Cptr) {
        if (Cptr[strlen (Cptr) - 4] != '.') {
          fprintf (MakeFptr, "    \\\n       $(LIB_DIR)\\%s.lib", Cptr);
          //
          // Add lib dependency for single module build
          //
          fprintf (gGlobals.ModuleMakefileFptr, " %sbuild", Cptr);
        } else {
          fprintf (MakeFptr, "    \\\n       $(LIB_DIR)\\%s", Cptr);
          //
          // Add lib dependency for single module build
          //
          Cptr[strlen (Cptr) - 4] = 0;
          fprintf (gGlobals.ModuleMakefileFptr, " %sbuild", Cptr);
        }
        //
        // Add libs dependency for mCurrentBuildItem 
        //
        AddDependency (*mCurrentBuildList, mCurrentBuildItem, Cptr, 0);
      }
    }
  }

  return STATUS_SUCCESS;
}

static
int
ProcessIncludeFiles (
  DSC_FILE *ComponentFile,
  FILE     *MakeFptr
  )
{
  INT8  Str[MAX_LINE_LEN];
  INT8  *Processor;
  INT8  *Platform;
  INT8  *SourceSelect;
  INT8  *CStart;
  INT8  *CEnd;
  INT8  CSave;
  INT8  *CopySourceSelect;

  //
  // Print a useful comment to the output makefile so the user knows where
  // the info came from
  //
  //fprintf (MakeFptr, "#\n# Tool-generated include dependencies from any include files in the\n");
  //fprintf (MakeFptr, "# [sources.*] sections of the component INF file\n#\n");

  Processor = GetSymbolValue (PROCESSOR);
  
  //
  // See if they defined SOURCE_SELECT=xxx,yyy in which case we'll
  // select each [sources.xxx] and [sources.yyy] files and process
  // them.
  //
  SourceSelect = GetSymbolValue (SOURCE_SELECT);

  if (SourceSelect != NULL) {
    //
    // Make a copy of the string and break it up (comma-separated) and
    // select each [sources.*] file from the INF.
    //
    CopySourceSelect = (INT8 *) malloc (strlen (SourceSelect) + 1);
    if (CopySourceSelect == NULL) {
      Error (NULL, 0, 0, NULL, "failed to allocate memory");
      return STATUS_ERROR;
    }

    strcpy (CopySourceSelect, SourceSelect);
    CStart  = CopySourceSelect;
    CEnd    = CStart;
    while (*CStart) {
      CEnd = CStart + 1;
      while (*CEnd && *CEnd != ',') {
        CEnd++;
      }

      CSave = *CEnd;
      *CEnd = 0;
      sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, CStart);
      ProcessIncludeFilesSingle (ComponentFile, MakeFptr, Str);
      //
      // Restore the terminator and advance
      //
      *CEnd   = CSave;
      CStart  = CEnd;
      if (*CStart) {
        CStart++;
      }
    }

    free (CopySourceSelect);

  } else {
    //
    // Find all the include files in the [sources.common] sections.
    //
    sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, COMMON_SECTION_NAME);
    ProcessIncludeFilesSingle (ComponentFile, MakeFptr, Str);
    //
    // Now process the [sources.$(PROCESSOR)] files.
    //
    sprintf (Str, "%s.%s", SOURCES_SECTION_NAME, Processor);
    ProcessIncludeFilesSingle (ComponentFile, MakeFptr, Str);
    //
    // Now process the [sources.$(PROCESSOR).$(PLATFORM)] files.
    //
    Platform = GetSymbolValue (PLATFORM);
    if (Platform != NULL) {
      sprintf (Str, "sources.%s.%s", Processor, Platform);
      ProcessIncludeFilesSingle (ComponentFile, MakeFptr, Str);
    }
  }
  
  fprintf (MakeFptr, "\n");
  return STATUS_SUCCESS;
}

int
ProcessIncludeFilesSingle (
  DSC_FILE  *ComponentFile,
  FILE      *MakeFptr,
  INT8      *SectionName
  )
{
  INT8            *Cptr;
  INT8            FileName[MAX_EXP_LINE_LEN];
  INT8            TempFileName[MAX_PATH];
  SECTION         *TempSect;
  INT8            Str[MAX_LINE_LEN];
  INT8            *OverridePath;
  FILE            *FPtr;

  TempSect = DSCFileFindSection (ComponentFile, SectionName);
  if (TempSect != NULL) {
    //
    // See if the SOURCE_OVERRIDE_PATH has been set. If it has, and
    // they have an include file that is overridden, then add the path
    // to it to the list of include paths (prepend).
    //
    OverridePath = GetSymbolValue (SOURCE_OVERRIDE_PATH);
    while (DSCFileGetLine (ComponentFile, Str, sizeof (Str)) != NULL) {
      Cptr = StripLine (Str);
      //
      // Don't process blank lines
      //
      if (*Cptr) {
        //
        // Expand symbols in the filename, then get its parts
        //
        ExpandSymbols (Cptr, FileName, sizeof (FileName), 0);
        AddFileSymbols (FileName);
        ReplaceSlash (FileName);
        if (IsIncludeFile (FileName)) {
          if ((OverridePath != NULL) && (!IsAbsolutePath (FileName))) {
            ReplaceSlash (OverridePath);
            strcpy (TempFileName, OverridePath);
            strcat (TempFileName, "\\");
            strcat (TempFileName, FileName);
            if ((FPtr = fopen (TempFileName, "rb")) != NULL) {
              fclose (FPtr);
              //
              // Null-terminate the file name at the last backslash and add that
              // to the beginning of the list of include paths.
              //
              for (Cptr = TempFileName + strlen (TempFileName) - 1;
                   (Cptr >= TempFileName) && (*Cptr != '\\');
                   Cptr--
                  )
                ;
              if (Cptr >= TempFileName) {
                *Cptr = 0;
              }
              fprintf (MakeFptr, "!IF EXIST(%s)\n", TempFileName);
              fprintf (MakeFptr, "INC = -I %s $(INC)\n", TempFileName);
              fprintf (MakeFptr, "!ENDIF\n");
            }
          }
          //
          // If absolute path already, don't prepend source directory
          //
          // if (IsAbsolutePath (FileName)) {
          //   fprintf (MakeFptr, "INC_DEPS = $(INC_DEPS) %s\n", FileName);
          // } else {
          //   fprintf (MakeFptr, "INC_DEPS = $(INC_DEPS) $(SOURCE_DIR)\\%s\n", FileName);
          // }
        }

        RemoveFileSymbols ();
      }
    }
  }

  return STATUS_SUCCESS;
}

static
void
FreeFileParts (
  FILE_NAME_PARTS *FP
  )
{
  if (FP != NULL) {
    if (FP->Path != NULL) {
      free (FP->Path);
    }

    if (FP->BaseName != NULL) {
      free (FP->BaseName);
    }

    if (FP->Extension != NULL) {
      free (FP->Extension);
    }
  }
}

static
FILE_NAME_PARTS *
GetFileParts (
  INT8 *FileName
  )
{
  FILE_NAME_PARTS *FP;
  INT8            *Cptr;
  INT8            CopyFileName[MAX_PATH];
  INT8            *FileNamePtr;

  strcpy (CopyFileName, FileName);
  FP = (FILE_NAME_PARTS *) malloc (sizeof (FILE_NAME_PARTS));
  if (FP == NULL) {
    Error (NULL, 0, 0, NULL, "failed to allocate memory");
    return NULL;
  }

  memset ((INT8 *) FP, 0, sizeof (FILE_NAME_PARTS));
  //
  // Get extension code
  //
  FP->ExtensionCode = GetSourceFileType (CopyFileName);
  //
  // Get drive if there
  //
  FileNamePtr = CopyFileName;
  if (FileNamePtr[1] == ':') {
    FP->Drive[0]  = FileNamePtr[0];
    FP->Drive[1]  = ':';
    FileNamePtr += 2;
  }
  //
  // Start at the end and work back
  //
  for (Cptr = FileNamePtr + strlen (FileNamePtr) - 1; (Cptr > FileNamePtr) && (*Cptr != '.'); Cptr--)
    ;

  if (*Cptr == '.') {
    //
    // Don't copy the dot
    //
    FP->Extension = (char *) malloc (strlen (Cptr));
    strcpy (FP->Extension, Cptr + 1);
    *Cptr = 0;
    Cptr--;
    StripTrailingSpaces (FP->Extension);
  } else {
    //
    // Create empty string for extension
    //
    FP->Extension     = (char *) malloc (1);
    FP->Extension[0]  = 0;
  }
  //
  // Now back up and get the base name (include the preceding '\')
  //
  for (; (Cptr > FileNamePtr) && (*Cptr != '\\'); Cptr--)
    ;
  FP->BaseName = (char *) malloc (strlen (Cptr) + 1);
  strcpy (FP->BaseName, Cptr);
  *Cptr = 0;
  Cptr--;
  //
  // Rest is path
  //
  if (Cptr >= FileNamePtr) {
    Cptr      = FileNamePtr;
    FP->Path  = (char *) malloc (strlen (Cptr) + 1);
    strcpy (FP->Path, Cptr);
  } else {
    FP->Path    = (char *) malloc (1);
    FP->Path[0] = 0;
  }

  return FP;
}

/*****************************************************************************
******************************************************************************/
static
int
WriteCommonMakefile (
  DSC_FILE  *DSCFile,
  FILE      *MakeFptr,
  INT8      *Processor
  )
{
  INT8    InLine[MAX_LINE_LEN];
  INT8    OutLine[MAX_EXP_LINE_LEN];
  SECTION *Sect;
  INT8    *Sym;
  int     i;
  //
  // Don't mess up the original file pointer, since we're processing it at a higher
  // level.
  //
  DSCFileSavePosition (DSCFile);
  //
  // Write the header to the file
  //
  for (i = 0; MakefileHeader[i] != NULL; i++) {
    fprintf (MakeFptr, "%s\n", MakefileHeader[i]);
  }

  fprintf (MakeFptr, "#\n# Hard-coded defines output by the tool\n#\n");
  //
  // First write the basics to the component's makefile. These includes
  // EFI_SOURCE, BIN_DIR, OUT_DIR, LIB_DIR, SOURCE_DIR, DEST_DIR.
  //
  Sym = GetSymbolValue (EFI_SOURCE);
  fprintf (MakeFptr, "%s       = %s\n", EFI_SOURCE, Sym);
  Sym = GetSymbolValue (BUILD_DIR);
  fprintf (MakeFptr, "%s        = %s\n", BUILD_DIR, Sym);
  Sym = GetSymbolValue (BIN_DIR);
  fprintf (MakeFptr, "%s          = %s\n", BIN_DIR, Sym);
  Sym = GetSymbolValue (OUT_DIR);
  fprintf (MakeFptr, "%s          = %s\n", OUT_DIR, Sym);
  Sym = GetSymbolValue (LIB_DIR);
  fprintf (MakeFptr, "%s          = %s\n", LIB_DIR, Sym);
  Sym = GetSymbolValue (SOURCE_DIR);
  fprintf (MakeFptr, "%s       = %s\n", SOURCE_DIR, Sym);
  Sym = GetSymbolValue (DEST_DIR);
  fprintf (MakeFptr, "%s         = %s\n", DEST_DIR, Sym);
  fprintf (MakeFptr, "\n");
  //
  // If there was a [makefile.common] section in the description file,
  // copy it (after symbol expansion) to the output file.
  //
  sprintf (InLine, "%s.%s", MAKEFILE_SECTION_NAME, COMMON_SECTION_NAME);
  Sect = DSCFileFindSection (DSCFile, InLine);
  if (Sect != NULL) {
    //
    // fprintf (MakeFptr, "# From the [makefile.common] section of the DSC file\n");
    // Read lines, expand, then dump out
    //
    while (DSCFileGetLine (DSCFile, InLine, sizeof (InLine)) != NULL) {
      //
      // Replace symbols
      //
      ExpandSymbols (InLine, OutLine, sizeof (OutLine), EXPANDMODE_RECURSIVE);
      fprintf (MakeFptr, OutLine);
    }
  }
  //
  // If there was a [makefile.platform] section in the description file,
  // copy it (after symbol expansion) to the output file.
  //
  sprintf (InLine, "%s.%s", MAKEFILE_SECTION_NAME, "Platform");
  Sect = DSCFileFindSection (DSCFile, InLine);
  if (Sect != NULL) {
    //
    // Read lines, expand, then dump out
    //
    while (DSCFileGetLine (DSCFile, InLine, sizeof (InLine)) != NULL) {
      //
      // Replace symbols
      //
      ExpandSymbols (InLine, OutLine, sizeof (OutLine), EXPANDMODE_RECURSIVE);
      fprintf (MakeFptr, OutLine);
    }
  }
  //
  // Do the same for any [makefile.$(PROCESSOR)]
  //
  sprintf (InLine, "%s.%s", MAKEFILE_SECTION_NAME, Processor);
  Sect = DSCFileFindSection (DSCFile, InLine);
  if (Sect != NULL) {
    //
    // Read lines, expand, then dump out
    //
    while (DSCFileGetLine (DSCFile, InLine, sizeof (InLine)) != NULL) {
      ExpandSymbols (InLine, OutLine, sizeof (OutLine), EXPANDMODE_RECURSIVE);
      fprintf (MakeFptr, OutLine);
    }
  }
  //
  // Same thing for [makefile.$(PROCESSOR).$(PLATFORM)]
  //
  Sym = GetSymbolValue (PLATFORM);
  if (Sym != NULL) {
    sprintf (InLine, "%s.%s.%s", MAKEFILE_SECTION_NAME, Processor, Sym);
    Sect = DSCFileFindSection (DSCFile, InLine);
    if (Sect != NULL) {
      //
      // Read lines, expand, then dump out
      //
      while (DSCFileGetLine (DSCFile, InLine, sizeof (InLine)) != NULL) {
        ExpandSymbols (InLine, OutLine, sizeof (OutLine), EXPANDMODE_RECURSIVE);
        fprintf (MakeFptr, OutLine);
      }
    }
  }
  
  fprintf (MakeFptr, "\n");
  DSCFileRestorePosition (DSCFile);
  return 0;
}

static
int
WriteComponentTypeBuildCommands (
  DSC_FILE *DSCFile,
  FILE     *MakeFptr,
  INT8     *SectionName
  )
/*++

Routine Description:
  
   Given a section name such as [build.ia32.library], find the section in
   the description file and copy the build commands.

Arguments:

  DSCFile     - section information on the main description file
  MakeFptr    - file pointer to the makefile we're writing to
  SectionName - name of the section we're to copy out to the makefile.

Returns:

  Always successful, since the section may be optional.

--*/
{
  SECTION *Sect;
  INT8    InLine[MAX_LINE_LEN];
  INT8    OutLine[MAX_EXP_LINE_LEN];
  
  //
  // Don't mess up the original file pointer, since we're processing it at a higher
  // level.
  //
  DSCFileSavePosition (DSCFile);
  Sect = DSCFileFindSection (DSCFile, SectionName);
  if (Sect != NULL) {
    //
    // Read lines, expand, then dump out
    //
    while (DSCFileGetLine (DSCFile, InLine, sizeof (InLine)) != NULL) {
      ExpandSymbols (
        InLine, 
        OutLine, 
        sizeof(OutLine), 
        EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
        );
      fprintf (MakeFptr, OutLine);
    }
  } else {
    Warning (
      NULL,
      0,
      0,
      GetSymbolValue (INF_FILENAME),
      "no [%s] build commands found in DSC file for component",
      SectionName
      );
  }

  DSCFileRestorePosition (DSCFile);
  return STATUS_SUCCESS;
}

/*****************************************************************************

******************************************************************************/
static
int
WriteCompileCommands (
  DSC_FILE  *DscFile,
  FILE      *MakeFptr,
  INT8      *FileName,
  INT8      *Processor
  )
{
  FILE_NAME_PARTS *File;
  SECTION         *Sect;
  INT8            BuildSectionName[40];
  INT8            InLine[MAX_LINE_LEN];
  INT8            OutLine[MAX_EXP_LINE_LEN];
  INT8            *SourceCompileType;
  char            *CPtr;
  char            *CPtr2;
  //
  // Determine the filename, then chop it up into its parts
  //
  File = GetFileParts (FileName);
  if (File != NULL) {
    //
    // Don't mess up the original file pointer, since we're processing it at a higher
    // level.
    //
    DSCFileSavePosition (DscFile);
    //
    // Option 1: SOURCE_COMPILE_TYPE=MyCompileSection
    //           Find a section of that name from which to get the compile
    //           commands for this source file. 
    //           Look for [compile.$(PROCESSOR).$(SOURCE_COMPILE_TYPE]
    // Option 2: COMPILE_SELECT=.c=MyCCompile,.asm=MyAsm
    //           Find a [compile.$(PROCESSOR).MyCompile] section from which to
    //           get the compile commands for this source file. 
    //           Look for [compile.$(PROCESSOR).MyCompile]
    // Option 3: Look for standard section types to compile the file by extension.
    //           Look for [compile.$(PROCESSOR).<extension>]
    //
    Sect = NULL;
    //
    // Option 1 - use SOURCE_COMPILE_TYPE variable
    //
    SourceCompileType = GetSymbolValue (SOURCE_COMPILE_TYPE);
    if (SourceCompileType != NULL) {
      sprintf (BuildSectionName, "compile.%s.%s", Processor, SourceCompileType);
      Sect = DSCFileFindSection (DscFile, BuildSectionName);
    }
    //
    // Option 2 - use COMPILE_SELECT variable
    //
    if (Sect == NULL) {
      SourceCompileType = GetSymbolValue (COMPILE_SELECT);
      if (SourceCompileType != NULL) {
        //
        // Parse the variable, which looks like COMPILE_SELECT=.c=MyCCompiler;.asm=MyAsm;
        // to find an entry with a matching file name extension. If you find one,
        // then use that name to find the section name.
        //
        CPtr = SourceCompileType;
        while (*CPtr && (Sect == NULL)) {
          //
          // See if we found a match with this source file name extension. File->Extension
          // does not include the dot, so skip the dot in the COMPILE_SELECT variable if there
          // is one.
          //
          if (*CPtr == '.') {
            CPtr++;
          }

          if (_strnicmp (CPtr, File->Extension, strlen (File->Extension)) == 0) {
            //
            // Found a file name extension match -- extract the name from the variable, for
            // example "MyCCompiler"
            //
            while (*CPtr && (*CPtr != '=')) {
              CPtr++;
            }

            if ((*CPtr != '=') || (CPtr[1] == 0)) {
              Error (NULL, 0, 0, SourceCompileType, "malformed COMPILE_SELECT variable");
              break;
            }

            CPtr++;
            sprintf (BuildSectionName, "compile.%s.", Processor);
            for (CPtr2 = BuildSectionName + strlen (BuildSectionName);
                 *CPtr && (*CPtr != ',') && (*CPtr != ';');
                 CPtr++
                ) {
              *CPtr2 = *CPtr;
              CPtr2++;
            }

            *CPtr2  = 0;
            Sect    = DSCFileFindSection (DscFile, BuildSectionName);
            if (Sect == NULL) {
              ParserError (
                0,
                BuildSectionName,
                "could not find section in DSC file - selected by COMPILE_SELECT variable"
                );
            }
          }

          //
          // Skip to next file name extension in the COMPILE_SELECT variable
          //
          while (*CPtr && (*CPtr != ';') && (*CPtr != ',')) {
            CPtr++;
          }

          if (*CPtr) {
            CPtr++;
          }
        }
      }
    }
    //
    // Option 3 - use "Compile.$(PROCESSOR).<Extension>" section
    //
    if (Sect == NULL) {
      sprintf (BuildSectionName, "compile.%s.%s", Processor, File->Extension);
      Sect = DSCFileFindSection (DscFile, BuildSectionName);
    }
    //
    // Should have found something by now unless it's an include (.h) file
    //
    if (Sect != NULL) {
      //
      // Temporarily add a FILE variable to the global symbol table. Omit the
      // extension.
      //
      sprintf (InLine, "%s%s%s", File->Drive, File->Path, File->BaseName);
      AddSymbol ("FILE", InLine, SYM_OVERWRITE | SYM_LOCAL | SYM_FILENAME);
      //
      // Read lines, expand (except SOURCE_DIR and DEST_DIR), then dump out
      //
      while (DSCFileGetLine (DscFile, InLine, sizeof (InLine)) != NULL) {
        ExpandSymbols (
          InLine,
          OutLine,
          sizeof (OutLine),
          EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
          );
        fprintf (MakeFptr, OutLine);
      }
      fprintf (MakeFptr, "\n");
    } else {
      //
      // Be nice and ignore include files
      //
      if (!IsIncludeFile (FileName)) {
        Error (
          NULL,
          0,
          0,
          NULL,
          "no compile commands section [%s] found in DSC file for %s",
          BuildSectionName,
          FileName
          );
      }
    }

    DSCFileRestorePosition (DscFile);
    FreeFileParts (File);
  }

  return STATUS_SUCCESS;
}

/*****************************************************************************
******************************************************************************/
static
int
SetFileExtension (
  INT8 *FileName,
  INT8 *Extension
  )
{
  INT8  *Cptr;

  Cptr = FileName + strlen (FileName) - 1;
  while ((Cptr > FileName) && (*Cptr != '.')) {
    Cptr--;

  }
  //
  // Better be a dot
  //
  if (*Cptr != '.') {
    Message (2, "Missing filename extension: %s", FileName);
    return STATUS_WARNING;
  }

  Cptr++;
  if (*Extension == '.') {
    Extension++;
  }

  strcpy (Cptr, Extension);
  return STATUS_SUCCESS;
}

/*****************************************************************************
******************************************************************************/
int
MakeFilePath (
  INT8 *FileName
  )
{
  INT8  *Cptr;
  INT8  SavedChar;
  INT8  BuildDir[MAX_PATH];
  INT8  CopyFileName[MAX_PATH];

  //
  // Expand symbols in the filename
  //
  if (ExpandSymbols (FileName, CopyFileName, sizeof (CopyFileName), EXPANDMODE_NO_UNDEFS)) {
    Error (NULL, 0, 0, NULL, "undefined symbols in file path: %s", FileName);
    return STATUS_ERROR;
  }
  //
  // Copy it back
  //
  strcpy (FileName, CopyFileName);
  //
  // To avoid creating $(BUILD_DIR) path, see if this path is the same as
  // $(BUILD_DIR), and if it is, see if build dir exists and skip over that
  // portion if it does
  //
  Cptr = GetSymbolValue (BUILD_DIR);
  if (Cptr != NULL) {
    if (_strnicmp (Cptr, FileName, strlen (Cptr)) == 0) {
      //
      // BUILD_DIR path. See if it exists
      //
      strcpy (BuildDir, FileName);
      BuildDir[strlen (Cptr)] = 0;
      if ((_mkdir (BuildDir) != 0) && (errno != EEXIST)) {
        Cptr = FileName;
      } else {
        //
        // Already done. Shortcut. Skip to next path so that we don't create
        // the BUILD_DIR as well.
        //
        Cptr = FileName + strlen (Cptr);
        if (*Cptr == '\\') {
          Cptr++;
        }
      }
    } else {
      //
      // Not build dir
      //
      Cptr = FileName;
    }
  } else {
    Cptr = FileName;
  }
  //
  // Create directories until done. Skip over "c:\" in the path if it exists
  //
  if (*Cptr && (*(Cptr + 1) == ':') && (*(Cptr + 2) == '\\')) {
    Cptr += 3;
  }

  for (;;) {
    for (; *Cptr && (*Cptr != '\\'); Cptr++)
      ;
    if (*Cptr) {
      SavedChar = *Cptr;
      *Cptr     = 0;
      if ((_mkdir (FileName) != 0)) {
        //
        //        Error (NULL, 0, 0, FileName, "failed to create directory");
        //        return 1;
        //
      }

      *Cptr = SavedChar;
      Cptr++;
    } else {
      break;
    }
  }

  return STATUS_SUCCESS;
}

/*****************************************************************************
******************************************************************************/
int
ExpandSymbols (
  INT8  *SourceLine,
  INT8  *DestLine,
  int   LineLen,
  int   ExpandMode
  )
{
  static int  NestDepth = 0;
  INT8        *FromPtr;
  INT8        *ToPtr;
  INT8        *SaveStart;
  INT8        *Cptr;
  INT8        *value;
  int         Expanded;
  int         ExpandedCount;
  INT8        *LocalDestLine;
  STATUS      Status;
  int         LocalLineLen;

  NestDepth++;
  Status        = STATUS_SUCCESS;
  LocalDestLine = (INT8 *) malloc (LineLen);
  if (LocalDestLine == NULL) {
    Error (__FILE__, __LINE__, 0, "application error", "memory allocation failed");
    NestDepth = 0;
    return STATUS_ERROR;
  }

  FromPtr = SourceLine;
  ToPtr   = LocalDestLine;
  //
  // Walk the entire line, replacing $(SYMBOL_NAME).
  //
  LocalLineLen  = LineLen;
  ExpandedCount = 0;
  while (*FromPtr && (LocalLineLen > 0)) {
    if ((*FromPtr == '$') && (*(FromPtr + 1) == '(')) {
      //
      // Save the start in case it's undefined, in which case we copy it as-is.
      //
      SaveStart = FromPtr;
      Expanded  = 0;
      //
      // Symbol expansion time. Find the end (no spaces allowed)
      //
      FromPtr += 2;
      for (Cptr = FromPtr; *Cptr && (*Cptr != ')'); Cptr++)
        ;
      if (*Cptr) {
        //
        // Truncate the string at the closing parenthesis for ease-of-use.
        // Then copy the string directly to the destination line in case we don't find
        // a definition for it.
        //
        *Cptr = 0;
        strcpy (ToPtr, SaveStart);
        if ((_stricmp (SOURCE_DIR, FromPtr) == 0) && (ExpandMode & EXPANDMODE_NO_SOURCEDIR)) {
          //
          // excluded this expansion
          //
        } else if ((_stricmp (DEST_DIR, FromPtr) == 0) && (ExpandMode & EXPANDMODE_NO_DESTDIR)) {
          //
          // excluded this expansion
          //
        } else if ((value = GetSymbolValue (FromPtr)) != NULL) {
          strcpy (ToPtr, value);
          LocalLineLen -= strlen (value);
          ToPtr += strlen (value);
          Expanded = 1;
          ExpandedCount++;
        } else if (ExpandMode & EXPANDMODE_NO_UNDEFS) {
          Error (NULL, 0, 0, "undefined symbol", "$(%s)", FromPtr);
          Status = STATUS_ERROR;
          goto Done;
        }
        
        //
        // Restore closing parenthesis, and advance to next character
        //
        *Cptr   = ')';
        if (!Expanded) {
          FromPtr = SaveStart + 1;
          ToPtr++;
        } else {
          FromPtr = Cptr + 1;
        }
      } else {
        Error (NULL, 0, 0, SourceLine, "missing closing parenthesis on symbol");
        strcpy (ToPtr, FromPtr);
        Status = STATUS_WARNING;
        goto Done;
      }
    } else {
      *ToPtr = *FromPtr;
      FromPtr++;
      ToPtr++;
      LocalLineLen--;
    }
  }

  if (*FromPtr == 0) {
    *ToPtr = 0;
  }

  //
  // If we're in recursive mode, and we expanded at least one string successfully,
  // then make a recursive call to try again.
  //
  if ((ExpandedCount != 0) && (Status == STATUS_SUCCESS) && (ExpandMode & EXPANDMODE_RECURSIVE) && (NestDepth < 2)) {
    Status = ExpandSymbols (LocalDestLine, DestLine, LineLen, ExpandMode);
    free (LocalDestLine);
    NestDepth = 0;
    return Status;
  }

Done:
  if (Status != STATUS_ERROR) {
    strcpy (DestLine, LocalDestLine);
  }

  NestDepth = 0;
  free (LocalDestLine);
  return Status;
}

INT8 *
GetSymbolValue (
  INT8 *SymbolName
  )
/*++

Routine Description:
  
  Look up a symbol in our symbol table.

Arguments:

  SymbolName - The name of symbol.

Returns:

  Pointer to the value of the symbol if found
  NULL if the symbol is not found

--*/
{
  SYMBOL  *Symbol;

  //
  // Scan once for file-level symbols
  //
  Symbol = gGlobals.Symbol;
  while (Symbol) {
    if ((_stricmp (SymbolName, Symbol->Name) == 0) && (Symbol->Type & SYM_FILE)) {
      return Symbol->Value;
    }

    Symbol = Symbol->Next;
  }
  //
  // Scan once for local symbols
  //
  Symbol = gGlobals.Symbol;
  while (Symbol) {
    if ((_stricmp (SymbolName, Symbol->Name) == 0) && (Symbol->Type & SYM_LOCAL)) {
      return Symbol->Value;
    }

    Symbol = Symbol->Next;
  }
  //
  // No local value found. Scan for globals.
  //
  Symbol = gGlobals.Symbol;
  while (Symbol) {
    if ((_stricmp (SymbolName, Symbol->Name) == 0) && (Symbol->Type & SYM_GLOBAL)) {
      return Symbol->Value;
    }

    Symbol = Symbol->Next;
  }
  //
  // For backwards-compatibility, if it's "GUID", return FILE_GUID value
  //
  if (_stricmp (SymbolName, GUID) == 0) {
    return GetSymbolValue (FILE_GUID);
  }

  return NULL;
}

static
int
RemoveLocalSymbols (
  VOID
  )
/*++

Routine Description:
  
  Remove all local symbols from the symbol table. Local symbols are those
  that are defined typically by the component's INF file.

Arguments:

  None.

Returns:

  Right now, never fails.

--*/
{
  SYMBOL  *Sym;
  int     FoundOne;

  do {
    FoundOne  = 0;
    Sym       = gGlobals.Symbol;
    while (Sym) {
      if (Sym->Type & SYM_LOCAL) {
        //
        // Going to delete it out from under ourselves, so break and restart
        //
        FoundOne = 1;
        RemoveSymbol (Sym->Name, SYM_LOCAL);
        break;
      }

      Sym = Sym->Next;
    }
  } while (FoundOne);
  return STATUS_SUCCESS;
}

static
int
RemoveFileSymbols (
  VOID
  )
/*++

Routine Description:
  
  Remove all file-level symbols from the symbol table. File-level symbols are 
  those that are defined on a source file line in an INF file.

Arguments:

  None.

Returns:

  Right now, never fails.

--*/
{
  SYMBOL  *Sym;
  int     FoundOne;

  do {
    FoundOne  = 0;
    Sym       = gGlobals.Symbol;
    while (Sym) {
      if (Sym->Type & SYM_FILE) {
        //
        // Going to delete it out from under ourselves, so break and restart
        //
        FoundOne = 1;
        RemoveSymbol (Sym->Name, SYM_FILE);
        break;
      }

      Sym = Sym->Next;
    }
  } while (FoundOne);
  return STATUS_SUCCESS;
}

static
STATUS
ParseGuidDatabaseFile (
  INT8 *FileName
  )
/*++

Routine Description:
  This function parses a GUID-to-basename text file (perhaps output by
  the GuidChk utility) to define additional symbols. The format of the 
  file should be:

  7BB28B99-61BB-11D5-9A5D-0090273FC14D EFI_DEFAULT_BMP_LOGO_GUID gEfiDefaultBmpLogoGuid
  
  This function parses the line and defines global symbol:

    EFI_DEFAULT_BMP_LOGO_GUID=7BB28B99-61BB-11D5-9A5D-0090273FC14D 
  
  This symbol (rather than the actual GUID) can then be used in INF files to 
  fix duplicate GUIDs

Arguments:
  FileName  - the name of the file to parse.

Returns:
  STATUS_ERROR    - could not open FileName
  STATUS_SUCCESS  - we opened the file

--*/
{
  FILE  *Fptr;
  INT8  Line[100];
  INT8  Guid[100];
  INT8  DefineName[80];

  Fptr = fopen (FileName, "r");
  if (Fptr == NULL) {
    Error (NULL, 0, 0, FileName, "failed to open input GUID database input file");
    return STATUS_ERROR;
  }

  while (fgets (Line, sizeof (Line), Fptr) != NULL) {
    //
    // Get the GUID string, skip the defined name (EFI_XXX_GUID), and get the
    // variable name (gWhateverProtocolGuid)
    //
    if (sscanf (Line, "%s %s %*s", Guid, DefineName) == 2) {
      AddSymbol (DefineName, Guid, SYM_GLOBAL);
    }
  }

  fclose (Fptr);
  return STATUS_SUCCESS;
}

/*****************************************************************************

  Returns:
     0 if successful standard add
    length of the parsed string if passed in " name = value  "
    < 0 on error

******************************************************************************/
int
AddSymbol (
  INT8    *Name,
  INT8    *Value,
  int     Mode
  )
{
  SYMBOL  *Symbol;
  SYMBOL  *NewSymbol;
  int     Len;
  INT8    *Start;
  INT8    *Cptr;
  INT8    CSave1;
  INT8    *SaveCptr1;
  INT8    CSave2;
  INT8    *SaveCptr2;
  INT8    ShortName[MAX_PATH];

  Len           = 0;
  SaveCptr1     = NULL;
  CSave1        = 0;
  SaveCptr2     = NULL;
  CSave2        = 0;

  ShortName[0]  = 0;
  //
  // Mode better be local or global symbol
  //
  if ((Mode & (SYM_LOCAL | SYM_GLOBAL | SYM_FILE)) == 0) {
    Error (NULL, 0, 0, "APP ERROR", "adding symbol '%s' that is not local, global, nor file level", Name);
    return -1;
  }
  //
  // If value pointer is null, then they passed us a line something like:
  //    varname = value, or simply var =
  //
  if (Value == NULL) {
    Start = Name;
    while (*Name && isspace (*Name)) {
      Name++;

    }

    if (!*Name) {
      return -1;
    }
    //
    // Find the end of the name. Either space or a '='.
    //
    for (Value = Name; *Value && !isspace (*Value) && (*Value != '='); Value++)
      ;
    if (!*Value) {
      return -1;
    }
    //
    // Look for the '='
    //
    Cptr = Value;
    while (*Value && (*Value != '=')) {
      Value++;
    }

    if (!*Value) {
      return -1;
    }

    //
    // Now truncate the name
    //
    CSave1    = *Cptr;
    SaveCptr1 = Cptr;
    *Cptr     = 0;    

    //
    // Skip over the = and then any spaces
    //
    Value++;
    while (*Value && isspace (*Value)) {
      Value++;

    }
    //
    // Find end of string, checking for quoted string
    //
    if (*Value == '\"') {
      Value++;
      for (Cptr = Value; *Cptr && *Cptr != '\"'; Cptr++)
        ;
    } else {
      for (Cptr = Value; *Cptr && !isspace (*Cptr); Cptr++)
        ;
    }
    //
    // Null terminate the value string
    //
    if (*Cptr) {
      Len = (int) (Cptr - Start) + 1;
      CSave2    = *Cptr;
      SaveCptr2 = Cptr;
      *Cptr     = 0;
    } else {
      Len = (int) (Cptr - Start);
    }
  }

  //
  // If file name or file path, and we're shortening, then print it
  //
  if ((Mode & (SYM_FILEPATH | SYM_FILENAME)) && (GetSymbolValue (SHORT_NAMES) != NULL)) {
    if (GetShortPathName (Value, ShortName, sizeof (ShortName)) > 0) {
      //
      // fprintf (stdout, "String value '%s' shortened to '%s'\n",
      //    Value, ShortName);
      //
      Value = ShortName;
    } else {
      //
      // fprintf (stdout, "WARNING: Failed to get short name for %s\n", Value);
      //
    }
  }
  //
  // We now have a symbol name and a value. Look for an existing variable of
  // the same type (global or local) and overwrite it.
  //
  Symbol = gGlobals.Symbol;
  while (Symbol) {
    //
    // Check for symbol name match
    //
    if (_stricmp (Name, Symbol->Name) == 0) {
      //
      // See if this symbol is of the same type (global or local) as what
      // they're requesting
      //
      if ((Symbol->Type & (SYM_LOCAL | SYM_GLOBAL)) == (Mode & (SYM_LOCAL | SYM_GLOBAL))) {
        //
        // Did they say we could overwrite it?
        //
        if (Mode & SYM_OVERWRITE) {
          free (Symbol->Value);
          Symbol->Value = (INT8 *) malloc (strlen (Value) + 1);
          if (Symbol->Value == NULL) {
            Error (NULL, 0, 0, NULL, "failed to allocate memory");
            return -1;
          }

          strcpy (Symbol->Value, Value);
          //
          // If value == "NULL", then make it a 0-length string
          //
          if (_stricmp (Symbol->Value, "NULL") == 0) {
            Symbol->Value[0] = 0;
          }

          return Len;
        } else {
          return STATUS_ERROR;
        }
      }
    }

    Symbol = Symbol->Next;
  }
  //
  // Does not exist, create a new one
  //
  NewSymbol = (SYMBOL *) malloc (sizeof (SYMBOL));
  if (NewSymbol == NULL) {
    Error (NULL, 0, 0, NULL, "failed to allocate memory");
    return -1;
  }

  memset ((INT8 *) NewSymbol, 0, sizeof (SYMBOL));
  NewSymbol->Name   = (INT8 *) malloc (strlen (Name) + 1);
  NewSymbol->Value  = (INT8 *) malloc (strlen (Value) + 1);
  //
  // Simply use the mode bits as the type.
  //
  NewSymbol->Type = Mode;
  if ((NewSymbol->Name == NULL) || (NewSymbol->Value == NULL)) {
    Error (NULL, 0, 0, NULL, "failed to allocate memory");
    return -1;
  }

  strcpy (NewSymbol->Name, Name);
  strcpy (NewSymbol->Value, Value);
  //
  // Remove trailing spaces
  //
  Cptr = NewSymbol->Value + strlen (NewSymbol->Value) - 1;
  while (Cptr > NewSymbol->Value) {
    if (isspace (*Cptr)) {
      *Cptr = 0;
      Cptr--;
    } else {
      break;
    }
  }
  //
  // Add it to the head of the list.
  //
  NewSymbol->Next = gGlobals.Symbol;
  gGlobals.Symbol = NewSymbol;
  //
  // If value == "NULL", then make it a 0-length string
  //
  if (_stricmp (NewSymbol->Value, "NULL") == 0) {
    NewSymbol->Value[0] = 0;
  }
  //
  // Restore the terminator we inserted if they passed in var=value
  //
  if (SaveCptr1 != NULL) {
    *SaveCptr1 = CSave1;
  }
  if (SaveCptr2 != NULL) {
    *SaveCptr2 = CSave2;
  }

  return Len;
}

/*****************************************************************************
******************************************************************************/
static
int
RemoveSymbol (
  INT8 *Name,
  INT8 SymbolType
  )
{
  SYMBOL  *Symbol;
  SYMBOL  *PrevSymbol;

  PrevSymbol  = NULL;
  Symbol      = gGlobals.Symbol;
  while (Symbol) {
    if ((_stricmp (Name, Symbol->Name) == 0) && (Symbol->Type & SymbolType)) {
      if (Symbol->Value) {
        free (Symbol->Value);
      }

      free (Symbol->Name);
      if (PrevSymbol) {
        PrevSymbol->Next = Symbol->Next;
      } else {
        gGlobals.Symbol = Symbol->Next;
      }

      free (Symbol);
      return STATUS_SUCCESS;
    }

    PrevSymbol  = Symbol;
    Symbol      = Symbol->Next;
  }

  return STATUS_WARNING;
}

#if 0

/*****************************************************************************
******************************************************************************/
static
void
FreeSections (
  SECTION *Sect
  )
{
  SECTION *Next;

  while (Sect != NULL) {
    Next = Sect->Next;
    if (Sect->Name != NULL) {
      delete[] Sect->Name;
    }

    delete Sect;
    Sect = Next;
  }
}
#endif

/*****************************************************************************
******************************************************************************/
static
INT8 *
StripLine (
  INT8 *Line
  )
{
  INT8  *Cptr;
  int   Len;

  Cptr = Line;
  //
  // Look for '#' comments in first character of line
  //
  if (*Cptr == '#') {
    *Cptr = 0;
    return Cptr;
  }

  while (isspace (*Cptr)) {
    Cptr++;
  }
  //
  // Hack off newlines
  //
  Len = strlen (Cptr);
  if ((Len > 0) && (Cptr[Len - 1] == '\n')) {
    Cptr[Len - 1] = 0;
  }
  //
  // Hack off trailing spaces
  //
  StripTrailingSpaces (Cptr);
  return Cptr;
}

/*****************************************************************************
  FUNCTION:  ProcessOptions()
  
  DESCRIPTION: Process the command-line options.  
******************************************************************************/
static
int
ProcessOptions (
  int   Argc,
  INT8  *Argv[]
  )
/*++

Routine Description:
  
  Process the command line options to this utility.

Arguments:

  Argc   - Standard Argc.
  Argv[] - Standard Argv.

Returns:

--*/
{
  INT8  *Cptr;
  int   FreeCwd;

  //
  // Clear out the options
  //
  memset ((INT8 *) &gGlobals, 0, sizeof (gGlobals));

  Argc--;
  Argv++;

  if (Argc == 0) {
    Usage ();
    return STATUS_ERROR;
  }
  //
  // Now process the arguments
  //
  while (Argc > 0) {

    if ((Argv[0][0] == '-') || (Argv[0][0] == '/')) {
      switch (Argv[0][1]) {
      //
      // -? or -h help option
      //
      case '?':
      case 'h':
      case 'H':
        Usage ();
        return STATUS_ERROR;

      //
      // /d symbol=name
      //
      case 'd':
      case 'D':
        //
        // Skip to next arg
        //
        Argc--;
        Argv++;
        if (Argc == 0) {
          Argv--;
          Error (NULL, 0, 0, NULL, "missing symbol definition with %c%c", Argv[0][0], Argv[0][1]);
          return STATUS_ERROR;
        } else {
          if (AddSymbol (Argv[0], NULL, SYM_OVERWRITE | SYM_GLOBAL) <= 0) {
            Warning (NULL, 0, 0, Argv[0], "failed to add symbol: %s");
          }
        }
        break;

      //
      // output makefile name
      //
      case 'm':
      case 'M':
        //
        // Skip to next arg
        //
        Argc--;
        Argv++;
        if (Argc == 0) {
          Argv--;
          Error (NULL, 0, 0, Argv[0], "missing output makefile name with option");
          Usage ();
          return STATUS_ERROR;
        } else {
          strcpy (gGlobals.MakefileName, Argv[0]);
        }
        break;

      //
      // Print a cross-reference file containing guid/basename/processor
      //
      case 'x':
      case 'X':
        //
        // Skip to next arg
        //
        Argc--;
        Argv++;
        if (Argc == 0) {
          Argv--;
          Error (NULL, 0, 0, Argv[0], "missing cross-reference output filename with option");
          Usage ();
          return STATUS_ERROR;
        } else {
          strcpy (gGlobals.XRefFileName, Argv[0]);
        }
        break;

      //
      // GUID database file to preparse
      //
      case 'g':
      case 'G':
        //
        // Skip to next arg
        //
        Argc--;
        Argv++;
        if (Argc == 0) {
          Argv--;
          Error (NULL, 0, 0, Argv[0], "missing input GUID database filename with option");
          Usage ();
          return STATUS_ERROR;
        } else {
          strcpy (gGlobals.GuidDatabaseFileName, Argv[0]);
        }
        break;

      //
      // Enable multi-thread build and specify the thread number
      //
      case 'n':
      case 'N':
        //
        // Skip to next arg
        //
        Argc--;
        Argv++;
        if (Argc == 0) {
          Argv--;
          Error (NULL, 0, 0, Argv[0], "missing input thread number with option");
          Usage ();
          return STATUS_ERROR;
        } else {
          gGlobals.ThreadNumber = atoi (Argv[0]);
          if (gGlobals.ThreadNumber == 0) {
            Argv--;
            Error (NULL, 0, 0, Argv[0], "input thread number should not be %s", Argv[1]);
            return STATUS_ERROR;
          } else if (gGlobals.ThreadNumber > MAXIMUM_WAIT_OBJECTS) {
            Argv--;
            Error (NULL, 0, 0, Argv[0], "input thread number should not greater than %d", MAXIMUM_WAIT_OBJECTS);
            return STATUS_ERROR;
          }
        }
        break;

      //
      // Specify the multi-thread build target
      //
      case 't':
      case 'T':
        //
        // Skip to next arg
        //
        Argc--;
        Argv++;
        if (Argc == 0) {
          Argv--;
          Error (NULL, 0, 0, Argv[0], "missing input build target with option");
          Usage ();
          return STATUS_ERROR;
        } else if (_stricmp (Argv[0], "all") == 0) {
          gGlobals.BuildTarget |= BUILD_TARGET_ALL;
        } else if (_stricmp (Argv[0], "libraries") == 0) {
          gGlobals.BuildTarget |= BUILD_TARGET_LIBRARIES;
        } else if (_stricmp (Argv[0], "components") == 0) {
          gGlobals.BuildTarget |= BUILD_TARGET_COMPONENTS;
        } else {
          Argv--;
          Error (NULL, 0, 0, Argv[0], "input build target not supported");
          Usage ();
        }
        break;

      case 'v':
      case 'V':
        gGlobals.Verbose = 1;
        break;

      default:
        Error (NULL, 0, 0, Argv[0], "unrecognized option");
        return STATUS_ERROR;
      }
    } else {
      break;
    }

    Argc--;
    Argv++;
  }
  //
  // Must be at least one arg left
  //
  if (Argc > 0) {
    gGlobals.DscFilename = Argv[0];
  }

  if (gGlobals.DscFilename == NULL) {
    Error (NULL, 0, 0, NULL, "must specify DSC filename on command line");
    return STATUS_ERROR;
  }
  //
  // Make a global symbol for the DSC filename
  //
  AddSymbol (DSC_FILENAME, gGlobals.DscFilename, SYM_GLOBAL | SYM_FILENAME);
  //
  // If no output makefile specified, take the default
  //
  if (gGlobals.MakefileName[0] == 0) {
    strcpy (gGlobals.MakefileName, MAKEFILE_OUT_NAME);
  }
  //
  // Get the current working directory and use it for the build directory.
  // Only do this if they have not defined it on the command line. Do the
  // same for the bin dir, output dir, and library directory.
  //
  Cptr = GetSymbolValue (BUILD_DIR);
  if (Cptr == NULL) {
    Cptr    = _getcwd (NULL, 0);
    FreeCwd = 1;
    AddSymbol (BUILD_DIR, Cptr, SYM_OVERWRITE | SYM_GLOBAL | SYM_FILEPATH);
  } else {
    ReplaceSlash (Cptr);
    FreeCwd = 0;
  }

  if (FreeCwd) {
    free (Cptr);
  }
  
  //
  // Default build target is all
  //
  if (gGlobals.BuildTarget == 0) {
    gGlobals.BuildTarget = BUILD_TARGET_ALL;
  }
  
  return 0;
}

/*****************************************************************************
******************************************************************************/
static
SYMBOL *
FreeSymbols (
  SYMBOL *Syms
  )
{
  SYMBOL  *Next;
  while (Syms) {

    if (Syms->Name != NULL) {
      free (Syms->Name);
    }

    if (Syms->Value != NULL) {
      free (Syms->Value);
    }

    Next = Syms->Next;
    free (Syms);
    Syms = Next;
  }

  return Syms;
}

/*****************************************************************************
******************************************************************************/
static
int
GetSourceFileType (
  INT8 *FileName
  )
{
  INT8  *Cptr;
  int   len;
  int   i;

  len = strlen (FileName);
  if (len == 0) {
    return FILETYPE_UNKNOWN;

  }

  Cptr = FileName + len - 1;
  while ((*Cptr != '.') && (Cptr >= FileName)) {
    Cptr--;

  }

  if (*Cptr == '.') {

    for (i = 0; mFileTypes[i].Extension != NULL; i++) {
      len = strlen (mFileTypes[i].Extension);
      if (_strnicmp (mFileTypes[i].Extension, Cptr, len) == 0) {
        if ((*(Cptr + len) == 0) || isspace (*(Cptr + len))) {
          return mFileTypes[i].FileType;
        }
      }
    }
  }

  return FILETYPE_UNKNOWN;
}
//
// Determine if a given file is a standard include file. If we don't know,
// then assume it's not.
//
static
int
IsIncludeFile (
  INT8 *FileName
  )
{
  INT8  *Cptr;
  int   len;
  int   i;

  len = strlen (FileName);
  if (len == 0) {
    return 0;
  }

  Cptr = FileName + len - 1;
  while ((*Cptr != '.') && (Cptr >= FileName)) {
    Cptr--;
  }

  if (*Cptr == '.') {
    //
    // Now go through the list of filename extensions and try to find
    // a match for this file extension.
    //
    for (i = 0; mFileTypes[i].Extension != NULL; i++) {
      len = strlen (mFileTypes[i].Extension);
      if (_strnicmp (mFileTypes[i].Extension, Cptr, len) == 0) {
        //
        // Make sure that's all there is to the filename extension.
        //
        if ((*(Cptr + len) == 0) || isspace (*(Cptr + len))) {
          return mFileTypes[i].FileFlags & FILE_FLAG_INCLUDE;
        }
      }
    }
  }

  return 0;
}

/*****************************************************************************
******************************************************************************/
static
void
StripTrailingSpaces (
  INT8 *Str
  )
{
  INT8  *Cptr;
  Cptr = Str + strlen (Str) - 1;
  while (Cptr > Str) {
    if (isspace (*Cptr)) {
      *Cptr = 0;
      Cptr--;
    } else {
      break;
    }
  }
}

/*****************************************************************************
******************************************************************************/
static
int
GetEfiSource (
  VOID
  )
{
  INT8  *EfiSource;

  //
  // Don't set it if the user specified it on the command line.
  //
  EfiSource = GetSymbolValue (EFI_SOURCE);
  if ( EfiSource != NULL) {
    ReplaceSlash (EfiSource);
    if (EfiSource[strlen (EfiSource) - 1] == '\\') {
      EfiSource[strlen (EfiSource) - 1] = 0;
    }    
    return STATUS_SUCCESS;
  }

  //
  // Get the environmental variable setting of EFI_SOURCE. 
  //
  EfiSource = getenv (EFI_SOURCE);
  if (EfiSource != NULL) {
    ReplaceSlash (EfiSource);
    if (EfiSource[strlen (EfiSource) - 1] == '\\') {
      EfiSource[strlen (EfiSource) - 1] = 0;
    }
    AddSymbol (EFI_SOURCE, EfiSource, SYM_GLOBAL | SYM_FILEPATH); 
    return STATUS_SUCCESS;
  }

  Error (NULL, 0, 0, NULL, "could not determine EFI_SOURCE");
  return STATUS_ERROR;
}

void
Message (
  UINT32  PrintMask,
  INT8    *Fmt,
  ...
  )
{
  INT8    Line[MAX_LINE_LEN];
  va_list List;

  va_start (List, Fmt);
  vsprintf (Line, Fmt, List);
  if (PrintMask & gGlobals.Verbose) {
    fprintf (stdout, "%s\n", Line);
  }

  va_end (List);
}

static
void
Usage (
  VOID
  )
{
  int         Index;
  const char  *Str[] = {
    UTILITY_NAME" "UTILITY_VERSION" - Intel Process DSC File Utility",
    "  Copyright (C), 2004 - 2008 Intel Corporation",
    
#if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) )
    "  Built from "UTILITY_BUILD", project of "UTILITY_VENDOR,
#endif
    "",
    "Usage:",
    "  "UTILITY_NAME" [OPTION]... DSCFILE",
    "Options:",
    "  -d var=value        to define symbol 'var' to 'value'",
    "  -v                  for verbose mode",
    "  -g filename         to preparse GUID listing file",
    "  -x filename         to create a cross-reference file",
    "  -n threadnumber     to build with multi-thread",
    "  -t target           to build the specified target:",
    "                      all, libraries or components",
    NULL
  };
  for (Index = 0; Str[Index] != NULL; Index++) {
    fprintf (stdout, "%s\n", Str[Index]);
  }
}

/*++

Routine Description:
  
  Process the [defines] section in the DSC file.

Arguments:

  DscFile - pointer to the DSCFile class that contains the relevant info.

Returns:

  0 if not necessarily an absolute path
  1 otherwise

--*/
static
int
ProcessDSCDefinesSection (
  DSC_FILE *DscFile
  )
{
  INT8    Line[MAX_LINE_LEN];
  INT8    Line2[MAX_EXP_LINE_LEN];
  INT8    *Cptr;
  SECTION *Sect;

  //
  // Look for a [defines] section and process it
  //
  Sect = DSCFileFindSection (DscFile, DEFINES_SECTION_NAME);
  if (Sect == NULL) {
    return STATUS_ERROR;
  }
  //
  // Read lines while they're valid
  //
  while (DSCFileGetLine (DscFile, Line, sizeof (Line)) != NULL) {
    //
    // Expand symbols on the line
    //
    if (ExpandSymbols (Line, Line2, sizeof (Line2), 0)) {
      return STATUS_ERROR;
    }
    //
    // Strip the line
    //
    Cptr = StripLine (Line2);
    if (*Cptr) {
      //
      // Make the assignment
      //
      AddSymbol (Line2, NULL, SYM_OVERWRITE | SYM_GLOBAL);
    }
  }

  return STATUS_SUCCESS;
}

int
IsAbsolutePath (
  char    *FileName
  )
/*++

Routine Description:
  
  Determine if a given filename contains the full path information.

Arguments:

  FileName - the name of the file, with symbol expanded.

Returns:

  0 if not necessarily an absolute path
  1 otherwise

--*/
{
  //
  // If the first character is a-z, and the second character is a colon, then
  // it is an absolute path.
  //
  if (isalpha (FileName[0]) && (FileName[1] == ':')) {
    return 1;
  }

  return 0;
}

SMART_FILE *
SmartOpen (
  char        *FileName
  )
{
  SMART_FILE     *SmartFile;
  FILE           *Fptr;
  int            FileSize;
  
  SmartFile = malloc (sizeof (SMART_FILE));
  if (SmartFile == NULL) { 
    return NULL;
  }
  memset (SmartFile, 0, sizeof (SMART_FILE));
  
  SmartFile->FileName = malloc (strlen (FileName) + 1);
  if (SmartFile->FileName == NULL){
    SmartFree (SmartFile); 
    return NULL;
  }
  strcpy (SmartFile->FileName, FileName);

  if ((Fptr = fopen (FileName, "r")) != NULL) {
    fseek (Fptr, 0, SEEK_END);
    FileSize = ftell (Fptr);
    fseek (Fptr, 0, SEEK_SET);
    SmartFile->FileContent = malloc (FileSize + 1);
    if (SmartFile->FileContent != NULL) {
      memset (SmartFile->FileContent, 0, FileSize + 1);
      //
      // Usually FileLength < FileSize, because in text mode, carriage return-linefeed
      // combinations are translated into single linefeeds on input
      //       
      SmartFile->FileLength = fread (SmartFile->FileContent, sizeof(char), FileSize, Fptr);
    }
    fclose (Fptr);
  }
  
  //
  // No previous output file content, re-create the file
  //
  if (SmartFile->FileContent == NULL) {
    if ((SmartFile->FilePtr = fopen (FileName, "w")) == NULL) {
      SmartFree (SmartFile);
      return NULL;
    }    
  }
  
  return SmartFile;
}

int
SmartWrite (
  SMART_FILE  *SmartFile,
  char        *String
  )
{
  int  StrLen;
  
  if (SmartFile->FilePtr != NULL) {
    return fprintf (SmartFile->FilePtr, "%s", String);
  } else {
    StrLen = strlen (String);
    if ((StrLen > SmartFile->FileLength - SmartFile->FilePosition) || 
       (_strnicmp (&SmartFile->FileContent[SmartFile->FilePosition], String, StrLen) != 0)) {
      //
      // file changed, need to re-create.
      //
      if ((SmartFile->FilePtr = fopen (SmartFile->FileName, "w")) == NULL) {
        Error (NULL, 0, 0, SmartFile->FileName, "could not open file for writing when SmartWrite");
        return -1;
      } else {
        SmartFile->FileContent[SmartFile->FilePosition] = 0;
        fprintf (SmartFile->FilePtr, "%s%s", SmartFile->FileContent, String);
        return StrLen;
      }      
    } else {
      SmartFile->FilePosition += StrLen;
      return StrLen;
    }
  }
}

void
SmartClose (
  SMART_FILE  *SmartFile
  )
{
  if ((SmartFile->FilePtr == NULL) && (SmartFile->FilePosition < SmartFile->FileLength)) {
    //
    // The new file is smaller than before, re-create it.
    //
    if ((SmartFile->FilePtr = fopen (SmartFile->FileName, "w")) == NULL) {
      Error (NULL, 0, 0, SmartFile->FileName, "could not open file for writing when SmartClose");
    } else {
      SmartFile->FileContent[SmartFile->FilePosition] = 0;
      fprintf (SmartFile->FilePtr, "%s", SmartFile->FileContent);
    }
  }
  
  SmartFree(SmartFile);
}
  
static
void
SmartFree (
  SMART_FILE  *SmartFile
  )
{
  if (SmartFile == NULL) {
    return;
  }
  
  if (SmartFile->FileName != NULL ) {
    free (SmartFile->FileName);
  }
  
  if (SmartFile->FileContent != NULL ) {
    free (SmartFile->FileContent);
  }

  if (SmartFile->FilePtr != NULL ) {
    fclose (SmartFile->FilePtr);
  } 

  free (SmartFile);
  
  return;
}

static 
int
AddModuleName (
  SYMBOL  **SymbolList,
  INT8    *ModuleName,
  INT8    *InfName
  )
/*++

Routine Description:
  
  Add module name in the global module list. 
  For the same module names, it is only added once.

Arguments:
  SymbolList : add name into this list
  ModuleName : point to one module name char string.
  InfName    : point to this module inf file name with path.

Returns:

  0 : Successfully add input name into the global list.
  other value : allocate memory failed.

--*/
{
  SYMBOL *CurrentSymbol;
  SYMBOL *LastSymbol;
  
  //
  // Get the global module list.
  //
  CurrentSymbol = *SymbolList;
  LastSymbol    = *SymbolList;
  
  //
  // Search whether this module name has been added into the global list.
  //
  while (CurrentSymbol != NULL) {
    if (_stricmp (CurrentSymbol->Name, ModuleName) == 0) {
      if ((CurrentSymbol->Value == NULL) && (InfName == NULL)) {
        break;
      } else if ((CurrentSymbol->Value != NULL) && (InfName != NULL) && \
        (_stricmp (CurrentSymbol->Value, InfName) == 0)) {
        break;
      }
    }
    LastSymbol    = CurrentSymbol;
    CurrentSymbol = CurrentSymbol->Next;
  }
  
  //
  // Add new module name in list.
  //
  if (CurrentSymbol == NULL) {
    CurrentSymbol = (SYMBOL *) malloc (sizeof (SYMBOL));
    if (CurrentSymbol == NULL) {
      Error (NULL, 0, 0, NULL, "failed to allocate memory");
      return -1;
    }
    memset ((INT8 *) CurrentSymbol, 0, sizeof (SYMBOL));

    if (ModuleName != NULL) {
      CurrentSymbol->Name   = (INT8 *) malloc (strlen (ModuleName) + 1);
      strcpy (CurrentSymbol->Name, ModuleName);
    }

    if (InfName != NULL) {
      CurrentSymbol->Value  = (INT8 *) malloc (strlen (InfName) + 1);
      strcpy (CurrentSymbol->Value, InfName);
    }
    
    if (LastSymbol == NULL) {   
      *SymbolList      = CurrentSymbol;
    } else {
      LastSymbol->Next = CurrentSymbol;
    }
  }

  return 0;
}

 
static 
void
ReplaceSlash (
  INT8    *Path
  )
/*++

Routine Description:
  
  Replace '/' with '\\'

Returns:

--*/
{
  while (*Path) {
    if (*Path == '/') {
      *Path = '\\';
    }
    Path++;
  }
}
