| /*++ | |
| 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: | |
| FWVolume.c | |
| Abstract: | |
| This module contains functionality to keep track of files destined for | |
| multiple firmware volues. It saves them up, and when told to, dumps the | |
| file names out to some files used as input to other utilities that | |
| actually generate the FVs. | |
| --*/ | |
| #include <windows.h> // for max_path definition | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdlib.h> // for malloc() | |
| #include "Common.h" | |
| #include "DSCFile.h" | |
| #include "FWVolume.h" | |
| #define FV_INF_DIR "FV_INF_DIR" // symbol for where we create the FV INF file | |
| #define FV_FILENAME "FV_FILENAME" // symbol for the current FV.INF filename | |
| #define EFI_BASE_ADDRESS "EFI_BASE_ADDRESS" | |
| #define DEFAULT_FV_INF_DIR "FV" // default dir for where we create the FV INF file | |
| #define DEFAULT_FV_DIR "$(BUILD_DIR)" // where the FV file comes from | |
| typedef struct { | |
| char *ComponentType; | |
| char *Extension; | |
| } COMP_TYPE_EXTENSION; | |
| // | |
| // Use a linked list of these to keep track of all the FV names used | |
| // | |
| typedef struct _FV_LIST { | |
| struct _FV_LIST *Next; | |
| char FVFileName[MAX_PATH]; | |
| char BaseAddress[MAX_LINE_LEN]; | |
| SMART_FILE *FVFilePtr; | |
| SMART_FILE *AprioriFilePtr; | |
| char *Processor; | |
| int ComponentsInstance; // highest [components.n] section with a file for this FV | |
| } FV_LIST; | |
| // | |
| // Use a linked list of these to keep track of all FFS files built. When | |
| // we're done, we turn the info into the FV INF files used to build the | |
| // firmware volumes. | |
| // | |
| typedef struct _FILE_LIST { | |
| struct _FILE_LIST *Next; | |
| char *FileName; | |
| char *BaseFileName; | |
| char *FVs; // from FV=x,y,z | |
| char *BaseName; // only needed for duplicate basename check | |
| char *Processor; // only needed for duplicate basename check | |
| char Apriori[100]; // of format "FVRecovery:1,FVMain:2" from APRIORI define | |
| char *Guid; // guid string | |
| int ComponentsInstance; // which [components.n] section it's in | |
| } FILE_LIST; | |
| typedef struct _LINKED_LIST { | |
| struct _LINKED_LIST *Next; | |
| void *Data; | |
| } LINKED_LIST; | |
| static FILE_LIST *mFileList; | |
| static FILE_LIST *mLastFile; | |
| static char *mXRefFileName = NULL; | |
| static FV_LIST *mNonFfsFVList = NULL; | |
| // | |
| // Whenever an FV name is referenced, then add it to our list of known | |
| // FV's using these. | |
| // | |
| static FV_LIST *mFVList = NULL; | |
| static FV_LIST *mFVListLast = NULL; | |
| // | |
| // We use this list so that from a given component type, we can determine | |
| // the name of the file on disk. For example, if we're given a file's | |
| // guid and base name, and we know it's a "bs_driver", then we can look | |
| // up "bs_driver" in this array and know that the file (after it's built) | |
| // name is GUID-BASENAME.DXE | |
| // | |
| static const COMP_TYPE_EXTENSION mCompTypeExtension[] = { | |
| { | |
| "bs_driver", | |
| ".dxe" | |
| }, | |
| { | |
| "rt_driver", | |
| ".dxe" | |
| }, | |
| { | |
| "sal_rt_driver", | |
| ".dxe" | |
| }, | |
| { | |
| "security_core", | |
| ".sec" | |
| }, | |
| { | |
| "pei_core", | |
| ".pei" | |
| }, | |
| { | |
| "pic_peim", | |
| ".pei" | |
| }, | |
| { | |
| "pe32_peim", | |
| ".pei" | |
| }, | |
| { | |
| "relocatable_peim", | |
| ".pei" | |
| }, | |
| { | |
| "binary", | |
| ".ffs" | |
| }, | |
| { | |
| "application", | |
| ".app" | |
| }, | |
| { | |
| "file", | |
| ".ffs" | |
| }, | |
| { | |
| "fvimagefile", | |
| ".fvi" | |
| }, | |
| { | |
| "rawfile", | |
| ".raw" | |
| }, | |
| { | |
| "apriori", | |
| ".ffs" | |
| }, | |
| { | |
| "combined_peim_driver", | |
| ".pei" | |
| }, | |
| { | |
| NULL, | |
| NULL | |
| } | |
| }; | |
| static | |
| void | |
| CFVFreeFileList ( | |
| VOID | |
| ); | |
| static | |
| char * | |
| UpperCaseString ( | |
| char *Str | |
| ); | |
| static | |
| BOOLEAN | |
| InSameFv ( | |
| char *FVs1, | |
| char *FVs2 | |
| ); | |
| static | |
| void | |
| AddFirmwareVolumes ( | |
| char *FVs, | |
| int ComponentsInstance | |
| ); | |
| static | |
| BOOLEAN | |
| OrderInFvList ( | |
| char *FvList, | |
| char *FvName, | |
| int *Order | |
| ); | |
| int | |
| GetBaseAddress ( | |
| char *Name, | |
| char *BaseAddress | |
| ) | |
| { | |
| char *Start; | |
| char *Cptr; | |
| char CSave; | |
| char *Value; | |
| Start = Name; | |
| while (*Name && isspace (*Name)) { | |
| Name++; | |
| } | |
| if (!*Name) { | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Find the end of the name. Either space or a '='. | |
| // | |
| for (Value = Name; *Value && !isspace (*Value) && (*Value != '='); Value++) | |
| ; | |
| if (!*Value) { | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Look for the '=' | |
| // | |
| Cptr = Value; | |
| while (*Value && (*Value != '=')) { | |
| Value++; | |
| } | |
| if (!*Value) { | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Now truncate the name | |
| // | |
| CSave = *Cptr; | |
| *Cptr = 0; | |
| if (_stricmp (Name, EFI_BASE_ADDRESS) != 0) { | |
| return STATUS_ERROR; | |
| } | |
| *Cptr = CSave; | |
| // | |
| // 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 | |
| // | |
| CSave = *Cptr; | |
| *Cptr = 0; | |
| strcpy (BaseAddress, Value); | |
| *Cptr = CSave; | |
| return STATUS_SUCCESS; | |
| } | |
| int | |
| CFVAddFVFile ( | |
| char *Name, | |
| char *ComponentType, | |
| char *FVs, | |
| int ComponentsInstance, | |
| char *FFSExt, | |
| char *Processor, | |
| char *Apriori, | |
| char *BaseName, | |
| char *Guid | |
| ) | |
| /*++ | |
| Routine Description: | |
| Add a file to the list of files in one or more firmware volumes. | |
| Arguments: | |
| Name - $(FILE_GUID)-$(BASE_NAME), or filename | |
| ComponentType - type of component being added. Required so we know the | |
| resultant file name after it has been built | |
| FVs - string of commma-separated FVs that the given file is | |
| to be added to. For example, FVs="FV0001,FV0002" | |
| FFSExt - FFS filename extension of the file after it has been built. | |
| This is passed in to us in case we don't know the default | |
| filename extension based on the component type. | |
| Processor - the target processor which the FV is being built for | |
| Apriori - pointer to the definition of APRIORI. For example APRIORI="FvRecovery:1,FvMain:4" | |
| Returns: | |
| STATUS_SUCCESS if successful | |
| --*/ | |
| { | |
| FILE_LIST *Ptr; | |
| char FileName[MAX_PATH]; | |
| char Str[MAX_PATH]; | |
| int i; | |
| char *Sym; | |
| // If they provided a filename extension for this type of file, then use it. | |
| // If they did not provide a filename extension, search our list for a | |
| // matching component type and use the extension appropriate for this | |
| // component type. | |
| // | |
| if (FFSExt == NULL) { | |
| // | |
| // They didn't give us a filename extension. Figure it out from the | |
| // component type. | |
| // | |
| for (i = 0; mCompTypeExtension[i].ComponentType != NULL; i++) { | |
| if (_stricmp (ComponentType, mCompTypeExtension[i].ComponentType) == 0) { | |
| FFSExt = mCompTypeExtension[i].Extension; | |
| break; | |
| } | |
| } | |
| // | |
| // If we don't know the file extension, then error out. Just means | |
| // the need to define "FFS_EXT = raw" in the component INF file. | |
| // | |
| if (mCompTypeExtension[i].ComponentType == NULL) { | |
| Error ( | |
| NULL, | |
| 0, | |
| 0, | |
| ComponentType, | |
| "unknown component type - must define FFS_EXT for built filename extension in component INF file" | |
| ); | |
| return STATUS_ERROR; | |
| } | |
| } | |
| // | |
| // We now have all the parts to the FFS filename. Prepend the path to it if | |
| // it's not a full pathname. | |
| // See if they overrode the default base directory for the FV files. | |
| // | |
| if (!IsAbsolutePath (Name)) { | |
| Sym = GetSymbolValue (FV_DIR); | |
| if (Sym == NULL) { | |
| Sym = DEFAULT_FV_DIR; | |
| } | |
| // | |
| // Create the file path. Something like $(BUILD_DIR)\$(PROCESSOR)\$(GUID)-$(BASE_NAME).ext | |
| // If the extension is non-zero length, then make sure there's a dot in it. | |
| // | |
| if ((strlen (FFSExt) > 0) && (FFSExt[0] != '.')) { | |
| sprintf (Str, "%s\\%s\\%s.%s", Sym, Processor, Name, FFSExt); | |
| } else { | |
| sprintf (Str, "%s\\%s\\%s%s", Sym, Processor, Name, FFSExt); | |
| } | |
| ExpandSymbols (Str, FileName, sizeof (FileName), EXPANDMODE_NO_UNDEFS); | |
| } else { | |
| strcpy (FileName, Name); | |
| } | |
| // | |
| // Traverse the list of files we have so far and make sure we don't have | |
| // any duplicate basenames. If the base name and processor match, then we'll | |
| // have build issues, so don't allow it. We also don't allow the same file GUID | |
| // in the same FV which will cause boot time error if we allow this. | |
| // | |
| Ptr = mFileList; | |
| while (Ptr != NULL) { | |
| if ((Ptr->BaseName != NULL) && (BaseName != NULL) && (_stricmp (BaseName, Ptr->BaseName) == 0)) { | |
| if ((Ptr->Processor != NULL) && (Processor != NULL) && (_stricmp (Processor, Ptr->Processor) == 0)) { | |
| Error (NULL, 0, 0, BaseName, "duplicate base name specified"); | |
| return STATUS_ERROR; | |
| } | |
| } | |
| if ((Ptr->Guid != NULL) && (Guid != NULL) && (_stricmp (Guid, Ptr->Guid) == 0)) { | |
| if ((Ptr->FVs != NULL) && (FVs != NULL) && (InSameFv (FVs, Ptr->FVs))) { | |
| Error (NULL, 0, 0, Guid, "duplicate Guid specified in the same FV for %s and %s", | |
| (Ptr->BaseName==NULL)?"Unknown":Ptr->BaseName, | |
| (BaseName==NULL)?"Unknown":BaseName); | |
| return STATUS_ERROR; | |
| } | |
| } | |
| Ptr = Ptr->Next; | |
| } | |
| // | |
| // Allocate a new structure so we can add this file to the list of | |
| // files. | |
| // | |
| Ptr = (FILE_LIST *) malloc (sizeof (FILE_LIST)); | |
| if (Ptr == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| memset ((char *) Ptr, 0, sizeof (FILE_LIST)); | |
| Ptr->FileName = (char *) malloc (strlen (FileName) + 1); | |
| if (Ptr->FileName == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Ptr->FileName, FileName); | |
| Ptr->ComponentsInstance = ComponentsInstance; | |
| // | |
| // Allocate memory to save the FV list if it's going into an FV. | |
| // | |
| if ((FVs != NULL) && (FVs[0] != 0)) { | |
| Ptr->FVs = (char *) malloc (strlen (FVs) + 1); | |
| if (Ptr->FVs == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Ptr->FVs, FVs); | |
| } | |
| Ptr->BaseFileName = (char *) malloc (strlen (Name) + 1); | |
| if (Ptr->BaseFileName == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Ptr->BaseFileName, Name); | |
| // | |
| // Allocate memory for the basename if they gave us one. May not have one | |
| // if the user is simply adding pre-existing binary files to the image. | |
| // | |
| if (BaseName != NULL) { | |
| Ptr->BaseName = (char *) malloc (strlen (BaseName) + 1); | |
| if (Ptr->BaseName == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Ptr->BaseName, BaseName); | |
| } | |
| // | |
| // Allocate memory for the processor name | |
| // | |
| if (Processor != NULL) { | |
| Ptr->Processor = (char *) malloc (strlen (Processor) + 1); | |
| if (Ptr->Processor == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Ptr->Processor, Processor); | |
| } | |
| // | |
| // Allocate memory for the guid name | |
| // | |
| if (Guid != NULL) { | |
| Ptr->Guid = (char *) malloc (strlen (Guid) + 1); | |
| if (Ptr->Guid == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory"); | |
| return STATUS_ERROR; | |
| } | |
| strcpy (Ptr->Guid, Guid); | |
| } | |
| // | |
| // If non-null apriori symbol, then save the apriori list for this file | |
| // | |
| if (Apriori != NULL) { | |
| strcpy (Ptr->Apriori, Apriori); | |
| } | |
| if (mFileList == NULL) { | |
| mFileList = Ptr; | |
| } else { | |
| mLastFile->Next = Ptr; | |
| } | |
| mLastFile = Ptr; | |
| // | |
| // Add these firmware volumes to the list of known firmware | |
| // volume names. | |
| // | |
| AddFirmwareVolumes (FVs, ComponentsInstance); | |
| return STATUS_SUCCESS; | |
| } | |
| void | |
| CFVConstructor ( | |
| VOID | |
| ) | |
| { | |
| mFileList = NULL; | |
| mLastFile = NULL; | |
| } | |
| void | |
| CFVDestructor ( | |
| VOID | |
| ) | |
| { | |
| CFVFreeFileList (); | |
| // | |
| // Free up our firmware volume list | |
| // | |
| while (mFVList != NULL) { | |
| mFVListLast = mFVList->Next; | |
| free (mFVList); | |
| mFVList = mFVListLast; | |
| } | |
| } | |
| static | |
| void | |
| CFVFreeFileList ( | |
| VOID | |
| ) | |
| { | |
| FILE_LIST *Next; | |
| while (mFileList != NULL) { | |
| if (mFileList->FileName != NULL) { | |
| free (mFileList->FileName); | |
| } | |
| if (mFileList->FVs != NULL) { | |
| free (mFileList->FVs); | |
| } | |
| free (mFileList->BaseFileName); | |
| if (mFileList->BaseName != NULL) { | |
| free (mFileList->BaseName); | |
| } | |
| if (mFileList->Processor != NULL) { | |
| free (mFileList->Processor); | |
| } | |
| if (mFileList->Guid != NULL) { | |
| free (mFileList->Guid); | |
| } | |
| Next = mFileList->Next; | |
| free (mFileList); | |
| mFileList = Next; | |
| } | |
| mFileList = NULL; | |
| } | |
| int | |
| CFVWriteInfFiles ( | |
| DSC_FILE *DSC, | |
| FILE *MakeFptr | |
| ) | |
| /*++ | |
| Routine Description: | |
| After processing all components in a DSC file, create the firmware | |
| volume INF files. We actually do a lot more here. | |
| * Create the FVxxx.inf file that is used by GenFvImage | |
| * Create the Apriori files for each firmware volume that requires one | |
| * Create makefile.out macros for FVxxx_FILES = FVxxx_FILES AnotherFile | |
| so you can do incremental builds of firmware volumes. | |
| * For each FV, emit its build commands to makefile.out | |
| Arguments: | |
| DSC - pointer to a DSC_FILE object to extract info from | |
| MakeFptr - pointer to the output makefile | |
| Returns: | |
| 0 if successful | |
| non-zero otherwise | |
| --*/ | |
| { | |
| FILE_LIST *FileListPtr; | |
| FV_LIST *FVList; | |
| FV_LIST *LastFVList; | |
| FV_LIST *FVPtr; | |
| SECTION *Section; | |
| char *StartCptr; | |
| char *EndCptr; | |
| char CSave; | |
| char Str[MAX_PATH]; | |
| char Line[MAX_LINE_LEN]; | |
| char ExpandedLine[MAX_LINE_LEN]; | |
| char FVDir[MAX_PATH]; | |
| FILE *XRefFptr; | |
| int AprioriCounter; | |
| int AprioriCount; | |
| int AprioriPosition; | |
| BOOLEAN AprioriFound; | |
| int ComponentsInstance; | |
| int ComponentCount; | |
| // | |
| // Use this to keep track of all the firmware volume names | |
| // | |
| FVList = NULL; | |
| LastFVList = NULL; | |
| // | |
| // See if they specified a FV directory to dump the FV files out to. If not, | |
| // then use the default. Then create the output directory. | |
| // | |
| StartCptr = GetSymbolValue (FV_INF_DIR); | |
| if (StartCptr == NULL) { | |
| ExpandSymbols (DEFAULT_FV_INF_DIR, FVDir, sizeof (FVDir), EXPANDMODE_NO_UNDEFS); | |
| } else { | |
| strcpy (FVDir, StartCptr); | |
| } | |
| // | |
| // Make sure the fv directory path ends in / | |
| // | |
| CSave = FVDir[strlen (FVDir) - 1]; | |
| if ((CSave != '\\') && (CSave != '/')) { | |
| strcat (FVDir, "\\"); | |
| } | |
| // | |
| // Traverse the list of all files, determine which FV each is in, then | |
| // write out the file's name to the output FVxxx.inf file. | |
| // | |
| for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) { | |
| // | |
| // Parse all the "FV1,FV2..." in the FVs | |
| // | |
| if (FileListPtr->FVs != NULL) { | |
| // | |
| // Process each fv this file is in | |
| // | |
| StartCptr = FileListPtr->FVs; | |
| while (*StartCptr) { | |
| EndCptr = StartCptr; | |
| while (*EndCptr && (*EndCptr != ',')) { | |
| EndCptr++; | |
| } | |
| CSave = *EndCptr; | |
| *EndCptr = 0; | |
| // | |
| // Ok, we have a fv name, now see if we've already opened | |
| // an fv output file of this name. | |
| // | |
| for (FVPtr = FVList; FVPtr != NULL; FVPtr = FVPtr->Next) { | |
| if (_stricmp (FVPtr->FVFileName, StartCptr) == 0) { | |
| break; | |
| } | |
| } | |
| // | |
| // If we didn't find one, then create a new one | |
| // | |
| if (FVPtr == NULL) { | |
| // | |
| // Create a new one, add it to the list | |
| // | |
| FVPtr = (FV_LIST *) malloc (sizeof (FV_LIST)); | |
| if (FVPtr == NULL) { | |
| Error (NULL, 0, 0, NULL, "failed to allocate memory for FV"); | |
| return STATUS_ERROR; | |
| } | |
| memset ((char *) FVPtr, 0, sizeof (FV_LIST)); | |
| // | |
| // Add it to the end of our list | |
| // | |
| if (FVList == NULL) { | |
| FVList = FVPtr; | |
| } else { | |
| LastFVList->Next = FVPtr; | |
| } | |
| LastFVList = FVPtr; | |
| // | |
| // Save the FV name in the FileName pointer so we can compare | |
| // for any future FV names specified. | |
| // | |
| strcpy (FVPtr->FVFileName, StartCptr); | |
| // | |
| // Add a symbol for the FV filename | |
| // | |
| UpperCaseString (FVPtr->FVFileName); | |
| AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE); | |
| // | |
| // Now create the FVx.inf filename from the fv name and | |
| // default filename extension. Dump it in the FV directory | |
| // as well. | |
| // | |
| strcpy (Str, FVDir); | |
| strcat (Str, FVPtr->FVFileName); | |
| strcat (Str, ".inf"); | |
| // | |
| // Create the directory path for our new fv.inf output file. | |
| // | |
| MakeFilePath (Str); | |
| if ((FVPtr->FVFilePtr = SmartOpen (Str)) == NULL) { | |
| Error (NULL, 0, 0, Str, "could not open FV output file"); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Now copy the [fv.$(FV).options] to the fv INF file | |
| // | |
| sprintf (Str, "fv.%s.options", StartCptr); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section != NULL) { | |
| SmartWrite (FVPtr->FVFilePtr, "[options]\n"); | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0); | |
| SmartWrite (FVPtr->FVFilePtr, ExpandedLine); | |
| GetBaseAddress (ExpandedLine, FVPtr->BaseAddress); | |
| } | |
| } else { | |
| Error (NULL, 0, 0, Str, "could not find FV section in description file"); | |
| } | |
| // | |
| // Copy the [fv.$(FV).attributes] to the fv INF file | |
| // | |
| sprintf (Str, "fv.%s.attributes", StartCptr); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section != NULL) { | |
| SmartWrite (FVPtr->FVFilePtr, "[attributes]\n"); | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0); | |
| SmartWrite (FVPtr->FVFilePtr, ExpandedLine); | |
| } | |
| } else { | |
| Error (NULL, 0, 0, Str, "Could not find FV section in description file"); | |
| } | |
| // | |
| // Start the files section | |
| // | |
| SmartWrite (FVPtr->FVFilePtr, "\n[files]\n"); | |
| } | |
| // | |
| // Now write the FV filename to the FV.inf file. Prepend $(PROCESSOR) on | |
| // it. | |
| // | |
| sprintf (ExpandedLine, "EFI_FILE_NAME = %s\n", FileListPtr->FileName); | |
| SmartWrite (FVPtr->FVFilePtr, ExpandedLine); | |
| // | |
| // Next FV on the FV list | |
| // | |
| *EndCptr = CSave; | |
| StartCptr = EndCptr; | |
| if (*StartCptr) { | |
| StartCptr++; | |
| } | |
| } | |
| } | |
| } | |
| // | |
| // Now we walk the list of firmware volumes and create the APRIORI list | |
| // file for it . | |
| // | |
| for (FVPtr = FVList; FVPtr != NULL; FVPtr = FVPtr->Next) { | |
| // | |
| // Run through all the files and count up how many are to be | |
| // added to the apriori list for this FV. Then when we're done | |
| // we'll make sure we processed them all. We do this in case they | |
| // skipped an apriori index for a given FV. | |
| // | |
| AprioriCount = 0; | |
| for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) { | |
| if (OrderInFvList (FileListPtr->Apriori, FVPtr->FVFileName, &AprioriPosition)) { | |
| // | |
| // Emit an error if the index was 0, or they didn't give one. | |
| // | |
| if (AprioriPosition == 0) { | |
| Error ( | |
| GetSymbolValue (DSC_FILENAME), | |
| 1, | |
| 0, | |
| "apriori indexes are 1-based", | |
| "component %s:APRIORI=%s", | |
| FileListPtr->BaseName, | |
| FileListPtr->Apriori | |
| ); | |
| } else { | |
| AprioriCount++; | |
| } | |
| } | |
| } | |
| // | |
| // Now scan the files as we increment our apriori index | |
| // | |
| AprioriCounter = 0; | |
| do { | |
| AprioriFound = 0; | |
| AprioriCounter++; | |
| for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) { | |
| // | |
| // If in the apriori list for this fv, print the name. Open the | |
| // file first if we have to. | |
| // | |
| if ((FileListPtr->Apriori[0] != 0) && | |
| (OrderInFvList (FileListPtr->Apriori, FVPtr->FVFileName, &AprioriPosition)) | |
| ) { | |
| if (AprioriPosition == AprioriCounter) { | |
| // | |
| // If we've already found one for this index, emit an error. Decrement the | |
| // count of how files we are to process so we don't emit another error for | |
| // a miscount below. | |
| // | |
| if (AprioriFound) { | |
| Error ( | |
| GetSymbolValue (DSC_FILENAME), | |
| 1, | |
| 0, | |
| "duplicate apriori index found", | |
| "%s:%d", | |
| FVPtr->FVFileName, | |
| AprioriCounter | |
| ); | |
| AprioriCount--; | |
| } | |
| AprioriFound = 1; | |
| // | |
| // Open the apriori output file if we haven't already | |
| // | |
| if (FVPtr->AprioriFilePtr == NULL) { | |
| strcpy (Str, FVDir); | |
| strcat (Str, FVPtr->FVFileName); | |
| strcat (Str, ".apr"); | |
| if ((FVPtr->AprioriFilePtr = SmartOpen (Str)) == NULL) { | |
| Error (NULL, 0, 0, Str, "could not open output Apriori file for writing"); | |
| return STATUS_ERROR; | |
| } | |
| } | |
| sprintf (ExpandedLine, "%s\n", FileListPtr->BaseFileName); | |
| SmartWrite (FVPtr->AprioriFilePtr, ExpandedLine); | |
| } | |
| } | |
| } | |
| } while (AprioriFound); | |
| // | |
| // See if they skipped an apriori position for this FV | |
| // | |
| if (AprioriCount != (AprioriCounter - 1)) { | |
| Error ( | |
| GetSymbolValue (DSC_FILENAME), | |
| 1, | |
| 0, | |
| "apriori index skipped", | |
| "%s:%d", | |
| FVPtr->FVFileName, | |
| AprioriCounter | |
| ); | |
| } | |
| } | |
| // | |
| // Traverse the list of all files again, and create a macro in the output makefile | |
| // that defines all the files in each fv. For example, for each FV file, create a line: | |
| // FV0001_FILES = $(FV_0001_FILES) xxxx-yyy.dxe. | |
| // This can then be used as a dependency in their makefile. | |
| // Also if they wanted us to dump a cross-reference, do that now. | |
| // | |
| if (mXRefFileName != NULL) { | |
| if ((XRefFptr = fopen (mXRefFileName, "w")) == NULL) { | |
| Message ( | |
| 0, | |
| "Failed to open cross-reference file '%s' for writing\n", | |
| mXRefFileName | |
| ); | |
| } | |
| } else { | |
| XRefFptr = NULL; | |
| } | |
| for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) { | |
| // | |
| // Parse all the "FV1,FV2..." in the FV field that came from FV=FVa,FVb,... on the | |
| // component line in the DSC file. | |
| // | |
| if (FileListPtr->FVs != NULL) { | |
| // | |
| // If generating a cross-reference file, dump the data | |
| // | |
| if (XRefFptr != NULL) { | |
| if ((FileListPtr->Guid != NULL) && (FileListPtr->BaseName != NULL) && (FileListPtr->Processor)) { | |
| fprintf ( | |
| XRefFptr, | |
| "%s %s %s\n", | |
| FileListPtr->Guid, | |
| FileListPtr->BaseName, | |
| FileListPtr->Processor | |
| ); | |
| } | |
| } | |
| // | |
| // Convert to uppercase since we're going to use the name as a macro variable name | |
| // in the makefile. | |
| // | |
| UpperCaseString (FileListPtr->FVs); | |
| // | |
| // Process each FV this file is in to write fvxxx_FILES = $(fvxxx_FILES) Guid-BaseName.ffs | |
| // | |
| StartCptr = FileListPtr->FVs; | |
| while (*StartCptr) { | |
| EndCptr = StartCptr; | |
| while (*EndCptr && (*EndCptr != ',')) { | |
| EndCptr++; | |
| } | |
| CSave = *EndCptr; | |
| *EndCptr = 0; | |
| fprintf ( | |
| MakeFptr, | |
| "%s_FILES = $(%s_FILES) %s\n", | |
| StartCptr, | |
| StartCptr, | |
| FileListPtr->FileName | |
| ); | |
| // | |
| // Next FV on the FV list | |
| // | |
| *EndCptr = CSave; | |
| StartCptr = EndCptr; | |
| if (*StartCptr) { | |
| StartCptr++; | |
| } | |
| } | |
| } | |
| } | |
| fprintf (MakeFptr, "\n"); | |
| // | |
| // Now go through the list of all NonFFS FVs they specified and search for | |
| // a [build.fv.$(FV)] or [build.fv] command and emit the commands to the | |
| // output makefile. Add them to the "fvs_0" target as well. | |
| // | |
| if (mNonFfsFVList != NULL) { | |
| fprintf (MakeFptr, "fvs_0 ::"); | |
| FVPtr = mNonFfsFVList; | |
| while (FVPtr != NULL) { | |
| fprintf (MakeFptr, " %s%s.fv", FVDir, FVPtr->FVFileName); | |
| FVPtr = FVPtr->Next; | |
| } | |
| fprintf (MakeFptr, "\n\n"); | |
| FVPtr = mNonFfsFVList; | |
| while (FVPtr != NULL) { | |
| // | |
| // Save the position in the file | |
| // | |
| DSCFileSavePosition (DSC); | |
| // | |
| // first try to find a build section specific for this fv. | |
| // | |
| sprintf (Str, "build.fv.%s", FVPtr->FVFileName); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section == NULL) { | |
| sprintf (Str, "build.fv"); | |
| Section = DSCFileFindSection (DSC, Str); | |
| } | |
| if (Section == NULL) { | |
| Warning ( | |
| NULL, | |
| 0, | |
| 0, | |
| NULL, | |
| "No [build.fv.%s] nor [%s] section found in description file for building %s", | |
| FVPtr->FVFileName, | |
| Str, | |
| FVPtr->FVFileName | |
| ); | |
| } else { | |
| // | |
| // Add a symbol for the FV filename | |
| // | |
| UpperCaseString (FVPtr->FVFileName); | |
| AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE); | |
| AddSymbol (EFI_BASE_ADDRESS, FVPtr->BaseAddress, SYM_LOCAL | SYM_OVERWRITE); | |
| // | |
| // Now copy the build commands from the section to the makefile | |
| // | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols ( | |
| Line, | |
| ExpandedLine, | |
| sizeof (ExpandedLine), | |
| EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR | |
| ); | |
| fprintf (MakeFptr, ExpandedLine); | |
| } | |
| } | |
| FVPtr = FVPtr->Next; | |
| DSCFileRestorePosition (DSC); | |
| } | |
| } | |
| // | |
| // Get the components count | |
| // | |
| ComponentCount = -1; | |
| for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) { | |
| if (FileListPtr->ComponentsInstance > ComponentCount) { | |
| ComponentCount = FileListPtr->ComponentsInstance; | |
| } | |
| } | |
| ComponentCount++; | |
| // | |
| // Now print firmware volumes build targets fvs_0, fvs_1 etc. | |
| // | |
| for (ComponentsInstance = 0; ComponentsInstance < ComponentCount; ComponentsInstance++) { | |
| fprintf (MakeFptr, "fvs_%d ::", ComponentsInstance); | |
| for (FVPtr = mFVList; FVPtr != NULL; FVPtr = FVPtr->Next) { | |
| if (FVPtr->ComponentsInstance == ComponentsInstance) { | |
| fprintf (MakeFptr, " %s%s.fv", FVDir, FVPtr->FVFileName); | |
| } | |
| } | |
| fprintf (MakeFptr, "\n\n"); | |
| } | |
| // | |
| // Create an "fvs" target that builds everything. It has to be a mix of | |
| // components and FV's in order. For example: | |
| // fvs :: components_0 fvs_0 components_1 fvs_1 | |
| // | |
| fprintf (MakeFptr, "fvs ::"); | |
| for (ComponentsInstance = 0; ComponentsInstance < ComponentCount; ComponentsInstance++) { | |
| fprintf (MakeFptr, " components_%d fvs_%d", ComponentsInstance, ComponentsInstance); | |
| } | |
| fprintf (MakeFptr, "\n\n"); | |
| // | |
| // Create a "components" target for build convenience. It should | |
| // look something like: | |
| // components : components_0 components_1... | |
| // | |
| if (ComponentCount > 0) { | |
| fprintf (MakeFptr, "components :"); | |
| for (ComponentsInstance = 0; ComponentsInstance < ComponentCount; ComponentsInstance++) { | |
| fprintf (MakeFptr, " components_%d", ComponentsInstance); | |
| } | |
| fprintf (MakeFptr, "\n\n"); | |
| } | |
| // | |
| // Now go through the list of all FV's defined and search for | |
| // a [build.fv.$(FV)] or [build.fv] command and emit the commands to the | |
| // output makefile. | |
| // | |
| FVPtr = mFVList; | |
| while (FVPtr != NULL) { | |
| if (FVPtr->FVFileName[0]) { | |
| // | |
| // Save the position in the file | |
| // | |
| DSCFileSavePosition (DSC); | |
| // | |
| // First try to find a build section specific for this FV. | |
| // | |
| sprintf (Str, "build.fv.%s", FVPtr->FVFileName); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section == NULL) { | |
| sprintf (Str, "build.fv"); | |
| Section = DSCFileFindSection (DSC, Str); | |
| } | |
| if (Section == NULL) { | |
| Error ( | |
| NULL, | |
| 0, | |
| 0, | |
| NULL, | |
| "no [build.fv.%s] nor [%s] section found in description file for building %s", | |
| FVPtr->FVFileName, | |
| Str, | |
| FVPtr->FVFileName | |
| ); | |
| } else { | |
| // | |
| // Add a symbol for the FV filename | |
| // | |
| UpperCaseString (FVPtr->FVFileName); | |
| AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE); | |
| AddSymbol (EFI_BASE_ADDRESS, FVPtr->BaseAddress, SYM_LOCAL | SYM_OVERWRITE); | |
| // | |
| // Now copy the build commands from the section to the makefile | |
| // | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols ( | |
| Line, | |
| ExpandedLine, | |
| sizeof (ExpandedLine), | |
| EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR | |
| ); | |
| fprintf (MakeFptr, ExpandedLine); | |
| } | |
| } | |
| DSCFileRestorePosition (DSC); | |
| } | |
| FVPtr = FVPtr->Next; | |
| } | |
| // | |
| // Close all the files and free up the memory | |
| // | |
| while (FVList != NULL) { | |
| FVPtr = FVList->Next; | |
| if (FVList->FVFilePtr != NULL) { | |
| SmartClose (FVList->FVFilePtr); | |
| } | |
| if (FVList->AprioriFilePtr != NULL) { | |
| SmartClose (FVList->AprioriFilePtr); | |
| } | |
| free (FVList); | |
| FVList = FVPtr; | |
| } | |
| while (mNonFfsFVList != NULL) { | |
| FVPtr = mNonFfsFVList->Next; | |
| free (mNonFfsFVList); | |
| mNonFfsFVList = FVPtr; | |
| } | |
| if (XRefFptr != NULL) { | |
| fclose (XRefFptr); | |
| } | |
| return STATUS_SUCCESS; | |
| } | |
| int | |
| NonFFSFVWriteInfFiles ( | |
| DSC_FILE *DSC, | |
| char *FileName | |
| ) | |
| /*++ | |
| Routine Description: | |
| Generate a Non FFS fv file. It can only some variables, | |
| or simply contains nothing except header. | |
| Arguments: | |
| DSC - pointer to a DSC_FILE object to extract info from | |
| FileName - pointer to the fv file | |
| Returns: | |
| STATUS_SUCCESS if successful | |
| non-STATUS_SUCCESS otherwise | |
| --*/ | |
| { | |
| FV_LIST *FVPtr; | |
| SECTION *Section; | |
| char *StartCptr; | |
| char *EndCptr; | |
| char CSave; | |
| char Str[MAX_PATH]; | |
| char Line[MAX_LINE_LEN]; | |
| char ExpandedLine[MAX_LINE_LEN]; | |
| char FVDir[MAX_PATH]; | |
| // | |
| // See if they specified a FV directory to dump the FV files out to. If not, | |
| // then use the default. Then create the output directory. | |
| // | |
| DSCFileSavePosition (DSC); | |
| StartCptr = GetSymbolValue (FV_INF_DIR); | |
| if (StartCptr == NULL) { | |
| ExpandSymbols (DEFAULT_FV_INF_DIR, FVDir, sizeof (FVDir), EXPANDMODE_NO_UNDEFS); | |
| } else { | |
| strcpy (FVDir, StartCptr); | |
| } | |
| // | |
| // Make sure the fv directory path ends in / | |
| // | |
| CSave = FVDir[strlen (FVDir) - 1]; | |
| if ((CSave != '\\') && (CSave != '/')) { | |
| strcat (FVDir, "\\"); | |
| } | |
| StartCptr = FileName; | |
| while (*StartCptr) { | |
| EndCptr = StartCptr; | |
| while (*EndCptr && (*EndCptr != ',')) { | |
| EndCptr++; | |
| } | |
| CSave = *EndCptr; | |
| *EndCptr = 0; | |
| // | |
| // Ok, we have a fv name, now see if we've already opened | |
| // an fv output file of this name. | |
| // | |
| for (FVPtr = mNonFfsFVList; FVPtr != NULL; FVPtr = FVPtr->Next) { | |
| if (_stricmp (FVPtr->FVFileName, StartCptr) == 0) { | |
| break; | |
| } | |
| } | |
| // | |
| // If there is already one with the same name, wrong | |
| // | |
| if (FVPtr != NULL) { | |
| DSCFileRestorePosition (DSC); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Create a new one, add it to the list | |
| // | |
| FVPtr = (FV_LIST *) malloc (sizeof (FV_LIST)); | |
| if (FVPtr == NULL) { | |
| Error (__FILE__, __LINE__, 0, "failed to allocate memory", NULL); | |
| DSCFileRestorePosition (DSC); | |
| return STATUS_ERROR; | |
| } | |
| memset ((char *) FVPtr, 0, sizeof (FV_LIST)); | |
| FVPtr->Next = mNonFfsFVList; | |
| mNonFfsFVList = FVPtr; | |
| // | |
| // Save the FV name in the FileName pointer so we can compare | |
| // for any future FV names specified. | |
| // | |
| strcpy (FVPtr->FVFileName, StartCptr); | |
| // | |
| // Add a symbol for the FV filename | |
| // | |
| UpperCaseString (FVPtr->FVFileName); | |
| AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE); | |
| // | |
| // Now create the FVx.inf filename from the fv name and | |
| // default filename extension. Dump it in the FV directory | |
| // as well. | |
| // | |
| strcpy (Str, FVDir); | |
| strcat (Str, FVPtr->FVFileName); | |
| strcat (Str, ".inf"); | |
| // | |
| // Create the directory path for our new fv.inf output file. | |
| // | |
| MakeFilePath (Str); | |
| if ((FVPtr->FVFilePtr = SmartOpen (Str)) == NULL) { | |
| Error (NULL, 0, 0, Str, "could not open FV output file"); | |
| DSCFileRestorePosition (DSC); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Now copy the [fv.fvfile.options] to the fv file | |
| // | |
| sprintf (Str, "fv.%s.options", StartCptr); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section != NULL) { | |
| SmartWrite (FVPtr->FVFilePtr, "[options]\n"); | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0); | |
| SmartWrite (FVPtr->FVFilePtr, ExpandedLine); | |
| GetBaseAddress (ExpandedLine, FVPtr->BaseAddress); | |
| } | |
| } else { | |
| Warning (NULL, 0, 0, NULL, "Could not find FV section '%s' in description file", Str); | |
| } | |
| // | |
| // Copy the [fv.fvfile.attributes] to the fv file | |
| // | |
| sprintf (Str, "fv.%s.attributes", StartCptr); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section != NULL) { | |
| SmartWrite (FVPtr->FVFilePtr, "[attributes]\n"); | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0); | |
| SmartWrite (FVPtr->FVFilePtr, ExpandedLine); | |
| } | |
| } else { | |
| Warning (NULL, 0, 0, NULL, "Could not find FV section '%s' in description file", Str); | |
| } | |
| // | |
| // Copy the [fv.fvfile.components] to the fv file | |
| // | |
| sprintf (Str, "fv.%s.components", StartCptr); | |
| Section = DSCFileFindSection (DSC, Str); | |
| if (Section != NULL) { | |
| SmartWrite (FVPtr->FVFilePtr, "[components]\n"); | |
| while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) { | |
| ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0); | |
| SmartWrite (FVPtr->FVFilePtr, ExpandedLine); | |
| } | |
| } else { | |
| // | |
| // An empty FV is allowed to contain nothing | |
| // | |
| } | |
| // | |
| // Close the file | |
| // | |
| SmartClose (FVPtr->FVFilePtr); | |
| // | |
| // Next FV in FileName | |
| // | |
| *EndCptr = CSave; | |
| StartCptr = EndCptr; | |
| if (*StartCptr) { | |
| StartCptr++; | |
| } | |
| } | |
| DSCFileRestorePosition (DSC); | |
| return STATUS_SUCCESS; | |
| } | |
| static | |
| void | |
| AddFirmwareVolumes ( | |
| char *FVs, | |
| int ComponentsInstance | |
| ) | |
| { | |
| FV_LIST *FvPtr; | |
| char *StartPtr; | |
| char *EndPtr; | |
| char SaveChar; | |
| if ((FVs != NULL) && (FVs[0] != 0)) { | |
| // | |
| // Extract each FV name from the string. It's from the DSC file "FV=FvRecover,FvMain" | |
| // | |
| StartPtr = FVs; | |
| while (*StartPtr != 0) { | |
| EndPtr = StartPtr; | |
| while (*EndPtr && (*EndPtr != ',')) { | |
| EndPtr++; | |
| } | |
| SaveChar = *EndPtr; | |
| *EndPtr = 0; | |
| // | |
| // Look through our list of known firmware volumes and see if we've | |
| // already added it. | |
| // | |
| for (FvPtr = mFVList; FvPtr != NULL; FvPtr = FvPtr->Next) { | |
| if (_stricmp (FvPtr->FVFileName, StartPtr) == 0) { | |
| break; | |
| } | |
| } | |
| // | |
| // If we didn't find a match, then create a new one | |
| // | |
| if (FvPtr == NULL) { | |
| FvPtr = malloc (sizeof (FV_LIST)); | |
| if (FvPtr == NULL) { | |
| Error (__FILE__, __LINE__, 0, "application error", "memory allocation failed"); | |
| return ; | |
| } | |
| memset (FvPtr, 0, sizeof (FV_LIST)); | |
| strcpy (FvPtr->FVFileName, StartPtr); | |
| if (mFVList == NULL) { | |
| mFVList = FvPtr; | |
| } else { | |
| mFVListLast->Next = FvPtr; | |
| } | |
| mFVListLast = FvPtr; | |
| } | |
| // | |
| // If this component's section number is higher than that of this | |
| // FV, then set the FV's to it. | |
| // | |
| if (FvPtr->ComponentsInstance < ComponentsInstance) { | |
| FvPtr->ComponentsInstance = ComponentsInstance; | |
| } | |
| // | |
| // If we found then end of the FVs in the string, then we're done. | |
| // Always restore the original string's contents. | |
| // | |
| if (SaveChar != 0) { | |
| *EndPtr = SaveChar; | |
| StartPtr = EndPtr + 1; | |
| } else { | |
| StartPtr = EndPtr; | |
| } | |
| } | |
| } | |
| } | |
| static | |
| BOOLEAN | |
| OrderInFvList ( | |
| char *FvList, | |
| char *FvName, | |
| int *Order | |
| ) | |
| { | |
| // | |
| // Given FvList of format "FV_a,FV_b,FV_c" or "FV_a:1,FV_b:2" and | |
| // FvName of format "FV_c", determine if FvName is in FvList. If | |
| // FV_a:1 format, then return the value after the colon. | |
| // | |
| while (*FvList) { | |
| // | |
| // If it matches for the length of FvName... | |
| // | |
| if (_strnicmp (FvList, FvName, strlen (FvName)) == 0) { | |
| // | |
| // Then see if the match string in FvList is terminated at the | |
| // same length. | |
| // | |
| if ((FvList[strlen (FvName)] == ',') || (FvList[strlen (FvName)] == 0)) { | |
| *Order = 0; | |
| return TRUE; | |
| } else if (FvList[strlen (FvName)] == ':') { | |
| *Order = atoi (FvList + strlen (FvName) + 1); | |
| return TRUE; | |
| } | |
| } | |
| // | |
| // Skip to next FV in the comma-separated list | |
| // | |
| while ((*FvList != ',') && (*FvList != 0)) { | |
| FvList++; | |
| } | |
| // | |
| // Skip over comma | |
| // | |
| if (*FvList == ',') { | |
| FvList++; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| static | |
| char * | |
| UpperCaseString ( | |
| char *Str | |
| ) | |
| { | |
| char *Cptr; | |
| for (Cptr = Str; *Cptr; Cptr++) { | |
| *Cptr = (char) toupper (*Cptr); | |
| } | |
| return Str; | |
| } | |
| static | |
| BOOLEAN | |
| InSameFv ( | |
| char *FVs1, | |
| char *FVs2 | |
| ) | |
| { | |
| char *StartCptr1; | |
| char *StartCptr2; | |
| char *EndCptr1; | |
| char *EndCptr2; | |
| char CSave1; | |
| char CSave2; | |
| // | |
| // Process each FV in first FV list | |
| // | |
| StartCptr1 = FVs1; | |
| while (*StartCptr1) { | |
| EndCptr1 = StartCptr1; | |
| while (*EndCptr1 && (*EndCptr1 != ',')) { | |
| EndCptr1++; | |
| } | |
| CSave1 = *EndCptr1; | |
| *EndCptr1 = 0; | |
| if (*StartCptr1) { | |
| // | |
| // Process each FV in second FV list | |
| // | |
| StartCptr2 = FVs2; | |
| while (*StartCptr2) { | |
| EndCptr2 = StartCptr2; | |
| while (*EndCptr2 && (*EndCptr2 != ',')) { | |
| EndCptr2++; | |
| } | |
| CSave2 = *EndCptr2; | |
| *EndCptr2 = 0; | |
| if (_stricmp (StartCptr1, StartCptr2) == 0) { | |
| *EndCptr1 = CSave1; | |
| *EndCptr2 = CSave2; | |
| return TRUE; | |
| } | |
| // | |
| // Next FV on the second FV list | |
| // | |
| *EndCptr2 = CSave2; | |
| StartCptr2 = EndCptr2; | |
| if (*StartCptr2) { | |
| StartCptr2++; | |
| } | |
| } | |
| } | |
| // | |
| // Next FV on the first FV list | |
| // | |
| *EndCptr1 = CSave1; | |
| StartCptr1 = EndCptr1; | |
| if (*StartCptr1) { | |
| StartCptr1++; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| int | |
| CFVSetXRefFileName ( | |
| char *FileName | |
| ) | |
| { | |
| mXRefFileName = FileName; | |
| return 0; | |
| } |