• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*++
2 
3 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 Module Name:
13 
14   FWVolume.c
15 
16 Abstract:
17 
18   This module contains functionality to keep track of files destined for
19   multiple firmware volues. It saves them up, and when told to, dumps the
20   file names out to some files used as input to other utilities that
21   actually generate the FVs.
22 
23 --*/
24 
25 #include <windows.h>                        // for max_path definition
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>                         // for malloc()
29 #include "Common.h"
30 #include "DSCFile.h"
31 #include "FWVolume.h"
32 
33 #define FV_INF_DIR          "FV_INF_DIR"    // symbol for where we create the FV INF file
34 #define FV_FILENAME         "FV_FILENAME"   // symbol for the current FV.INF filename
35 #define EFI_BASE_ADDRESS    "EFI_BASE_ADDRESS"
36 #define DEFAULT_FV_INF_DIR  "FV"            // default dir for where we create the FV INF file
37 #define DEFAULT_FV_DIR      "$(BUILD_DIR)"  // where the FV file comes from
38 
39 typedef struct {
40   char  *ComponentType;
41   char  *Extension;
42 } COMP_TYPE_EXTENSION;
43 
44 //
45 // Use a linked list of these to keep track of all the FV names used
46 //
47 typedef struct _FV_LIST {
48   struct _FV_LIST *Next;
49   char            FVFileName[MAX_PATH];
50   char            BaseAddress[MAX_LINE_LEN];
51   SMART_FILE      *FVFilePtr;
52   SMART_FILE      *AprioriFilePtr;
53   char            *Processor;
54   int             ComponentsInstance; // highest [components.n] section with a file for this FV
55 } FV_LIST;
56 
57 //
58 // Use a linked list of these to keep track of all FFS files built. When
59 // we're done, we turn the info into the FV INF files used to build the
60 // firmware volumes.
61 //
62 typedef struct _FILE_LIST {
63   struct _FILE_LIST *Next;
64   char              *FileName;
65   char              *BaseFileName;
66   char              *FVs;               // from FV=x,y,z
67   char              *BaseName;          // only needed for duplicate basename check
68   char              *Processor;         // only needed for duplicate basename check
69   char              Apriori[100];       // of format "FVRecovery:1,FVMain:2" from APRIORI define
70   char              *Guid;              // guid string
71   int               ComponentsInstance; // which [components.n] section it's in
72 } FILE_LIST;
73 
74 typedef struct _LINKED_LIST {
75   struct _LINKED_LIST *Next;
76   void                *Data;
77 } LINKED_LIST;
78 
79 static FILE_LIST                  *mFileList;
80 static FILE_LIST                  *mLastFile;
81 static char                       *mXRefFileName  = NULL;
82 static FV_LIST                    *mNonFfsFVList  = NULL;
83 
84 //
85 // Whenever an FV name is referenced, then add it to our list of known
86 // FV's using these.
87 //
88 static FV_LIST                    *mFVList      = NULL;
89 static FV_LIST                    *mFVListLast  = NULL;
90 
91 //
92 // We use this list so that from a given component type, we can determine
93 // the name of the file on disk. For example, if we're given a file's
94 // guid and base name, and we know it's a "bs_driver", then we can look
95 // up "bs_driver" in this array and know that the file (after it's built)
96 // name is GUID-BASENAME.DXE
97 //
98 static const COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
99   {
100     "bs_driver",
101     ".dxe"
102   },
103   {
104     "rt_driver",
105     ".dxe"
106   },
107   {
108     "sal_rt_driver",
109     ".dxe"
110   },
111   {
112     "security_core",
113     ".sec"
114   },
115   {
116     "pei_core",
117     ".pei"
118   },
119   {
120     "pic_peim",
121     ".pei"
122   },
123   {
124     "pe32_peim",
125     ".pei"
126   },
127   {
128     "relocatable_peim",
129     ".pei"
130   },
131   {
132     "binary",
133     ".ffs"
134   },
135   {
136     "application",
137     ".app"
138   },
139   {
140     "file",
141     ".ffs"
142   },
143   {
144     "fvimagefile",
145     ".fvi"
146   },
147   {
148     "rawfile",
149     ".raw"
150   },
151   {
152     "apriori",
153     ".ffs"
154   },
155   {
156     "combined_peim_driver",
157     ".pei"
158   },
159   {
160     NULL,
161     NULL
162   }
163 };
164 
165 static
166 void
167 CFVFreeFileList (
168   VOID
169   );
170 
171 static
172 char                              *
173 UpperCaseString (
174   char *Str
175   );
176 
177 static
178 BOOLEAN
179 InSameFv (
180   char  *FVs1,
181   char  *FVs2
182 );
183 
184 static
185 void
186 AddFirmwareVolumes (
187   char          *FVs,
188   int           ComponentsInstance
189   );
190 
191 static
192 BOOLEAN
193 OrderInFvList (
194   char    *FvList,
195   char    *FvName,
196   int     *Order
197   );
198 
199 int
GetBaseAddress(char * Name,char * BaseAddress)200 GetBaseAddress (
201   char *Name,
202   char *BaseAddress
203   )
204 {
205   char  *Start;
206   char  *Cptr;
207   char  CSave;
208   char  *Value;
209 
210   Start = Name;
211   while (*Name && isspace (*Name)) {
212     Name++;
213   }
214 
215   if (!*Name) {
216     return STATUS_ERROR;
217   }
218   //
219   // Find the end of the name. Either space or a '='.
220   //
221   for (Value = Name; *Value && !isspace (*Value) && (*Value != '='); Value++)
222     ;
223   if (!*Value) {
224     return STATUS_ERROR;
225   }
226   //
227   // Look for the '='
228   //
229   Cptr = Value;
230   while (*Value && (*Value != '=')) {
231     Value++;
232   }
233 
234   if (!*Value) {
235     return STATUS_ERROR;
236   }
237   //
238   // Now truncate the name
239   //
240   CSave = *Cptr;
241   *Cptr = 0;
242   if (_stricmp (Name, EFI_BASE_ADDRESS) != 0) {
243     return STATUS_ERROR;
244   }
245 
246   *Cptr = CSave;
247   //
248   // Skip over the = and then any spaces
249   //
250   Value++;
251   while (*Value && isspace (*Value)) {
252     Value++;
253   }
254   //
255   // Find end of string, checking for quoted string
256   //
257   if (*Value == '\"') {
258     Value++;
259     for (Cptr = Value; *Cptr && *Cptr != '\"'; Cptr++)
260       ;
261   } else {
262     for (Cptr = Value; *Cptr && !isspace (*Cptr); Cptr++)
263       ;
264   }
265   //
266   // Null terminate the value string
267   //
268   CSave = *Cptr;
269   *Cptr = 0;
270   strcpy (BaseAddress, Value);
271   *Cptr = CSave;
272 
273   return STATUS_SUCCESS;
274 }
275 
276 int
CFVAddFVFile(char * Name,char * ComponentType,char * FVs,int ComponentsInstance,char * FFSExt,char * Processor,char * Apriori,char * BaseName,char * Guid)277 CFVAddFVFile (
278   char  *Name,
279   char  *ComponentType,
280   char  *FVs,
281   int   ComponentsInstance,
282   char  *FFSExt,
283   char  *Processor,
284   char  *Apriori,
285   char  *BaseName,
286   char  *Guid
287   )
288 /*++
289 
290 Routine Description:
291 
292   Add a file to the list of files in one or more firmware volumes.
293 
294 Arguments:
295 
296   Name          - $(FILE_GUID)-$(BASE_NAME), or filename
297   ComponentType - type of component being added. Required so we know the
298                   resultant file name after it has been built
299   FVs           - string of commma-separated FVs that the given file is
300                   to be added to. For example, FVs="FV0001,FV0002"
301   FFSExt        - FFS filename extension of the file after it has been built.
302                   This is passed in to us in case we don't know the default
303                   filename extension based on the component type.
304   Processor     - the target processor which the FV is being built for
305   Apriori       - pointer to the definition of APRIORI. For example APRIORI="FvRecovery:1,FvMain:4"
306 
307 Returns:
308 
309   STATUS_SUCCESS if successful
310 
311 --*/
312 {
313   FILE_LIST *Ptr;
314   char      FileName[MAX_PATH];
315   char      Str[MAX_PATH];
316   int       i;
317   char      *Sym;
318 
319   // If they provided a filename extension for this type of file, then use it.
320   // If they did not provide a filename extension, search our list for a
321   // matching component type and use the extension appropriate for this
322   // component type.
323   //
324   if (FFSExt == NULL) {
325     //
326     // They didn't give us a filename extension. Figure it out from the
327     // component type.
328     //
329     for (i = 0; mCompTypeExtension[i].ComponentType != NULL; i++) {
330       if (_stricmp (ComponentType, mCompTypeExtension[i].ComponentType) == 0) {
331         FFSExt = mCompTypeExtension[i].Extension;
332         break;
333       }
334     }
335     //
336     // If we don't know the file extension, then error out. Just means
337     // the need to define "FFS_EXT = raw" in the component INF file.
338     //
339     if (mCompTypeExtension[i].ComponentType == NULL) {
340       Error (
341         NULL,
342         0,
343         0,
344         ComponentType,
345         "unknown component type - must define FFS_EXT for built filename extension in component INF file"
346         );
347       return STATUS_ERROR;
348     }
349   }
350   //
351   // We now have all the parts to the FFS filename. Prepend the path to it if
352   // it's not a full pathname.
353   // See if they overrode the default base directory for the FV files.
354   //
355   if (!IsAbsolutePath (Name)) {
356     Sym = GetSymbolValue (FV_DIR);
357     if (Sym == NULL) {
358       Sym = DEFAULT_FV_DIR;
359     }
360     //
361     // Create the file path. Something like $(BUILD_DIR)\$(PROCESSOR)\$(GUID)-$(BASE_NAME).ext
362     // If the extension is non-zero length, then make sure there's a dot in it.
363     //
364     if ((strlen (FFSExt) > 0) && (FFSExt[0] != '.')) {
365       sprintf (Str, "%s\\%s\\%s.%s", Sym, Processor, Name, FFSExt);
366     } else {
367       sprintf (Str, "%s\\%s\\%s%s", Sym, Processor, Name, FFSExt);
368     }
369 
370     ExpandSymbols (Str, FileName, sizeof (FileName), EXPANDMODE_NO_UNDEFS);
371   } else {
372     strcpy (FileName, Name);
373   }
374   //
375   // Traverse the list of files we have so far and make sure we don't have
376   // any duplicate basenames. If the base name and processor match, then we'll
377   // have build issues, so don't allow it. We also don't allow the same file GUID
378   // in the same FV which will cause boot time error if we allow this.
379   //
380   Ptr = mFileList;
381   while (Ptr != NULL) {
382     if ((Ptr->BaseName != NULL) && (BaseName != NULL) && (_stricmp (BaseName, Ptr->BaseName) == 0)) {
383       if ((Ptr->Processor != NULL) && (Processor != NULL) && (_stricmp (Processor, Ptr->Processor) == 0)) {
384         Error (NULL, 0, 0, BaseName, "duplicate base name specified");
385         return STATUS_ERROR;
386       }
387     }
388 
389     if ((Ptr->Guid != NULL) && (Guid != NULL) && (_stricmp (Guid, Ptr->Guid) == 0)) {
390       if ((Ptr->FVs != NULL) && (FVs != NULL) && (InSameFv (FVs, Ptr->FVs))) {
391         Error (NULL, 0, 0, Guid, "duplicate Guid specified in the same FV for %s and %s",
392                (Ptr->BaseName==NULL)?"Unknown":Ptr->BaseName,
393                (BaseName==NULL)?"Unknown":BaseName);
394         return STATUS_ERROR;
395       }
396     }
397 
398     Ptr = Ptr->Next;
399   }
400   //
401   // Allocate a new structure so we can add this file to the list of
402   // files.
403   //
404   Ptr = (FILE_LIST *) malloc (sizeof (FILE_LIST));
405   if (Ptr == NULL) {
406     Error (NULL, 0, 0, NULL, "failed to allocate memory");
407     return STATUS_ERROR;
408   }
409 
410   memset ((char *) Ptr, 0, sizeof (FILE_LIST));
411   Ptr->FileName = (char *) malloc (strlen (FileName) + 1);
412   if (Ptr->FileName == NULL) {
413     Error (NULL, 0, 0, NULL, "failed to allocate memory");
414     return STATUS_ERROR;
415   }
416 
417   strcpy (Ptr->FileName, FileName);
418   Ptr->ComponentsInstance = ComponentsInstance;
419   //
420   // Allocate memory to save the FV list if it's going into an FV.
421   //
422   if ((FVs != NULL) && (FVs[0] != 0)) {
423     Ptr->FVs = (char *) malloc (strlen (FVs) + 1);
424     if (Ptr->FVs == NULL) {
425       Error (NULL, 0, 0, NULL, "failed to allocate memory");
426       return STATUS_ERROR;
427     }
428 
429     strcpy (Ptr->FVs, FVs);
430   }
431 
432   Ptr->BaseFileName = (char *) malloc (strlen (Name) + 1);
433   if (Ptr->BaseFileName == NULL) {
434     Error (NULL, 0, 0, NULL, "failed to allocate memory");
435     return STATUS_ERROR;
436   }
437 
438   strcpy (Ptr->BaseFileName, Name);
439   //
440   // Allocate memory for the basename if they gave us one. May not have one
441   // if the user is simply adding pre-existing binary files to the image.
442   //
443   if (BaseName != NULL) {
444     Ptr->BaseName = (char *) malloc (strlen (BaseName) + 1);
445     if (Ptr->BaseName == NULL) {
446       Error (NULL, 0, 0, NULL, "failed to allocate memory");
447       return STATUS_ERROR;
448     }
449 
450     strcpy (Ptr->BaseName, BaseName);
451   }
452   //
453   // Allocate memory for the processor name
454   //
455   if (Processor != NULL) {
456     Ptr->Processor = (char *) malloc (strlen (Processor) + 1);
457     if (Ptr->Processor == NULL) {
458       Error (NULL, 0, 0, NULL, "failed to allocate memory");
459       return STATUS_ERROR;
460     }
461 
462     strcpy (Ptr->Processor, Processor);
463   }
464   //
465   // Allocate memory for the guid name
466   //
467   if (Guid != NULL) {
468     Ptr->Guid = (char *) malloc (strlen (Guid) + 1);
469     if (Ptr->Guid == NULL) {
470       Error (NULL, 0, 0, NULL, "failed to allocate memory");
471       return STATUS_ERROR;
472     }
473 
474     strcpy (Ptr->Guid, Guid);
475   }
476   //
477   // If non-null apriori symbol, then save the apriori list for this file
478   //
479   if (Apriori != NULL) {
480     strcpy (Ptr->Apriori, Apriori);
481   }
482 
483   if (mFileList == NULL) {
484     mFileList = Ptr;
485   } else {
486     mLastFile->Next = Ptr;
487   }
488 
489   mLastFile = Ptr;
490   //
491   // Add these firmware volumes to the list of known firmware
492   // volume names.
493   //
494   AddFirmwareVolumes (FVs, ComponentsInstance);
495 
496   return STATUS_SUCCESS;
497 }
498 
499 void
CFVConstructor(VOID)500 CFVConstructor (
501   VOID
502   )
503 {
504   mFileList = NULL;
505   mLastFile = NULL;
506 }
507 
508 void
CFVDestructor(VOID)509 CFVDestructor (
510   VOID
511   )
512 {
513   CFVFreeFileList ();
514   //
515   // Free up our firmware volume list
516   //
517   while (mFVList != NULL) {
518     mFVListLast = mFVList->Next;
519     free (mFVList);
520     mFVList = mFVListLast;
521   }
522 }
523 
524 static
525 void
CFVFreeFileList(VOID)526 CFVFreeFileList (
527   VOID
528   )
529 {
530   FILE_LIST *Next;
531   while (mFileList != NULL) {
532     if (mFileList->FileName != NULL) {
533       free (mFileList->FileName);
534     }
535 
536     if (mFileList->FVs != NULL) {
537       free (mFileList->FVs);
538     }
539 
540     free (mFileList->BaseFileName);
541     if (mFileList->BaseName != NULL) {
542       free (mFileList->BaseName);
543     }
544 
545     if (mFileList->Processor != NULL) {
546       free (mFileList->Processor);
547     }
548 
549     if (mFileList->Guid != NULL) {
550       free (mFileList->Guid);
551     }
552 
553     Next = mFileList->Next;
554     free (mFileList);
555     mFileList = Next;
556   }
557 
558   mFileList = NULL;
559 }
560 
561 int
CFVWriteInfFiles(DSC_FILE * DSC,FILE * MakeFptr)562 CFVWriteInfFiles (
563   DSC_FILE  *DSC,
564   FILE      *MakeFptr
565   )
566 /*++
567 
568 Routine Description:
569 
570   After processing all components in a DSC file, create the firmware
571   volume INF files. We actually do a lot more here.
572 
573   * Create the FVxxx.inf file that is used by GenFvImage
574   * Create the Apriori files for each firmware volume that requires one
575   * Create makefile.out macros for FVxxx_FILES = FVxxx_FILES AnotherFile
576     so you can do incremental builds of firmware volumes.
577   * For each FV, emit its build commands to makefile.out
578 
579 Arguments:
580 
581   DSC       - pointer to a DSC_FILE object to extract info from
582   MakeFptr  - pointer to the output makefile
583 
584 Returns:
585 
586   0  if successful
587   non-zero otherwise
588 
589 --*/
590 {
591   FILE_LIST *FileListPtr;
592   FV_LIST   *FVList;
593   FV_LIST   *LastFVList;
594   FV_LIST   *FVPtr;
595   SECTION   *Section;
596   char      *StartCptr;
597   char      *EndCptr;
598   char      CSave;
599   char      Str[MAX_PATH];
600   char      Line[MAX_LINE_LEN];
601   char      ExpandedLine[MAX_LINE_LEN];
602   char      FVDir[MAX_PATH];
603   FILE      *XRefFptr;
604   int       AprioriCounter;
605   int       AprioriCount;
606   int       AprioriPosition;
607   BOOLEAN   AprioriFound;
608   int       ComponentsInstance;
609   int       ComponentCount;
610 
611   //
612   // Use this to keep track of all the firmware volume names
613   //
614   FVList      = NULL;
615   LastFVList  = NULL;
616   //
617   // See if they specified a FV directory to dump the FV files out to. If not,
618   // then use the default. Then create the output directory.
619   //
620   StartCptr = GetSymbolValue (FV_INF_DIR);
621   if (StartCptr == NULL) {
622     ExpandSymbols (DEFAULT_FV_INF_DIR, FVDir, sizeof (FVDir), EXPANDMODE_NO_UNDEFS);
623   } else {
624     strcpy (FVDir, StartCptr);
625   }
626   //
627   // Make sure the fv directory path ends in /
628   //
629   CSave = FVDir[strlen (FVDir) - 1];
630   if ((CSave != '\\') && (CSave != '/')) {
631     strcat (FVDir, "\\");
632   }
633   //
634   // Traverse the list of all files, determine which FV each is in, then
635   // write out the file's name to the output FVxxx.inf file.
636   //
637   for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) {
638     //
639     // Parse all the "FV1,FV2..." in the FVs
640     //
641     if (FileListPtr->FVs != NULL) {
642       //
643       // Process each fv this file is in
644       //
645       StartCptr = FileListPtr->FVs;
646       while (*StartCptr) {
647         EndCptr = StartCptr;
648         while (*EndCptr && (*EndCptr != ',')) {
649           EndCptr++;
650         }
651 
652         CSave     = *EndCptr;
653         *EndCptr  = 0;
654         //
655         // Ok, we have a fv name, now see if we've already opened
656         // an fv output file of this name.
657         //
658         for (FVPtr = FVList; FVPtr != NULL; FVPtr = FVPtr->Next) {
659           if (_stricmp (FVPtr->FVFileName, StartCptr) == 0) {
660             break;
661           }
662         }
663         //
664         // If we didn't find one, then create a new one
665         //
666         if (FVPtr == NULL) {
667           //
668           // Create a new one, add it to the list
669           //
670           FVPtr = (FV_LIST *) malloc (sizeof (FV_LIST));
671           if (FVPtr == NULL) {
672             Error (NULL, 0, 0, NULL, "failed to allocate memory for FV");
673             return STATUS_ERROR;
674           }
675 
676           memset ((char *) FVPtr, 0, sizeof (FV_LIST));
677           //
678           // Add it to the end of our list
679           //
680           if (FVList == NULL) {
681             FVList = FVPtr;
682           } else {
683             LastFVList->Next = FVPtr;
684           }
685 
686           LastFVList = FVPtr;
687           //
688           // Save the FV name in the FileName pointer so we can compare
689           // for any future FV names specified.
690           //
691           strcpy (FVPtr->FVFileName, StartCptr);
692 
693           //
694           // Add a symbol for the FV filename
695           //
696           UpperCaseString (FVPtr->FVFileName);
697           AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE);
698           //
699           // Now create the FVx.inf filename from the fv name and
700           // default filename extension. Dump it in the FV directory
701           // as well.
702           //
703           strcpy (Str, FVDir);
704           strcat (Str, FVPtr->FVFileName);
705           strcat (Str, ".inf");
706           //
707           // Create the directory path for our new fv.inf output file.
708           //
709           MakeFilePath (Str);
710           if ((FVPtr->FVFilePtr = SmartOpen (Str)) == NULL) {
711             Error (NULL, 0, 0, Str, "could not open FV output file");
712             return STATUS_ERROR;
713           }
714           //
715           // Now copy the [fv.$(FV).options] to the fv INF file
716           //
717           sprintf (Str, "fv.%s.options", StartCptr);
718           Section = DSCFileFindSection (DSC, Str);
719           if (Section != NULL) {
720             SmartWrite (FVPtr->FVFilePtr, "[options]\n");
721             while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
722               ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0);
723               SmartWrite (FVPtr->FVFilePtr, ExpandedLine);
724               GetBaseAddress (ExpandedLine, FVPtr->BaseAddress);
725             }
726           } else {
727             Error (NULL, 0, 0, Str, "could not find FV section in description file");
728           }
729           //
730           // Copy the [fv.$(FV).attributes] to the fv INF file
731           //
732           sprintf (Str, "fv.%s.attributes", StartCptr);
733           Section = DSCFileFindSection (DSC, Str);
734           if (Section != NULL) {
735             SmartWrite (FVPtr->FVFilePtr, "[attributes]\n");
736             while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
737               ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0);
738               SmartWrite (FVPtr->FVFilePtr, ExpandedLine);
739             }
740           } else {
741             Error (NULL, 0, 0, Str, "Could not find FV section in description file");
742           }
743           //
744           // Start the files section
745           //
746           SmartWrite (FVPtr->FVFilePtr, "\n[files]\n");
747         }
748         //
749         // Now write the FV filename to the FV.inf file. Prepend $(PROCESSOR) on
750         // it.
751         //
752         sprintf (ExpandedLine, "EFI_FILE_NAME = %s\n", FileListPtr->FileName);
753         SmartWrite (FVPtr->FVFilePtr, ExpandedLine);
754 
755         //
756         // Next FV on the FV list
757         //
758         *EndCptr  = CSave;
759         StartCptr = EndCptr;
760         if (*StartCptr) {
761           StartCptr++;
762         }
763       }
764     }
765   }
766   //
767   // Now we walk the list of firmware volumes and create the APRIORI list
768   // file for it .
769   //
770   for (FVPtr = FVList; FVPtr != NULL; FVPtr = FVPtr->Next) {
771     //
772     // Run through all the files and count up how many are to be
773     // added to the apriori list for this FV. Then when we're done
774     // we'll make sure we processed them all. We do this in case they
775     // skipped an apriori index for a given FV.
776     //
777     AprioriCount = 0;
778     for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) {
779       if (OrderInFvList (FileListPtr->Apriori, FVPtr->FVFileName, &AprioriPosition)) {
780         //
781         // Emit an error if the index was 0, or they didn't give one.
782         //
783         if (AprioriPosition == 0) {
784           Error (
785             GetSymbolValue (DSC_FILENAME),
786             1,
787             0,
788             "apriori indexes are 1-based",
789             "component %s:APRIORI=%s",
790             FileListPtr->BaseName,
791             FileListPtr->Apriori
792             );
793         } else {
794           AprioriCount++;
795         }
796 
797       }
798     }
799     //
800     // Now scan the files as we increment our apriori index
801     //
802     AprioriCounter = 0;
803     do {
804       AprioriFound = 0;
805       AprioriCounter++;
806       for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) {
807         //
808         // If in the apriori list for this fv, print the name. Open the
809         // file first if we have to.
810         //
811         if ((FileListPtr->Apriori[0] != 0) &&
812             (OrderInFvList (FileListPtr->Apriori, FVPtr->FVFileName, &AprioriPosition))
813             ) {
814           if (AprioriPosition == AprioriCounter) {
815             //
816             // If we've already found one for this index, emit an error. Decrement the
817             // count of how files we are to process so we don't emit another error for
818             // a miscount below.
819             //
820             if (AprioriFound) {
821               Error (
822                 GetSymbolValue (DSC_FILENAME),
823                 1,
824                 0,
825                 "duplicate apriori index found",
826                 "%s:%d",
827                 FVPtr->FVFileName,
828                 AprioriCounter
829                 );
830               AprioriCount--;
831             }
832 
833             AprioriFound = 1;
834             //
835             // Open the apriori output file if we haven't already
836             //
837             if (FVPtr->AprioriFilePtr == NULL) {
838               strcpy (Str, FVDir);
839               strcat (Str, FVPtr->FVFileName);
840               strcat (Str, ".apr");
841               if ((FVPtr->AprioriFilePtr = SmartOpen (Str)) == NULL) {
842                 Error (NULL, 0, 0, Str, "could not open output Apriori file for writing");
843                 return STATUS_ERROR;
844               }
845             }
846 
847             sprintf (ExpandedLine, "%s\n", FileListPtr->BaseFileName);
848             SmartWrite (FVPtr->AprioriFilePtr, ExpandedLine);
849           }
850         }
851       }
852     } while (AprioriFound);
853     //
854     // See if they skipped an apriori position for this FV
855     //
856     if (AprioriCount != (AprioriCounter - 1)) {
857       Error (
858         GetSymbolValue (DSC_FILENAME),
859         1,
860         0,
861         "apriori index skipped",
862         "%s:%d",
863         FVPtr->FVFileName,
864         AprioriCounter
865         );
866     }
867   }
868   //
869   // Traverse the list of all files again, and create a macro in the output makefile
870   // that defines all the files in each fv. For example, for each FV file, create a line:
871   // FV0001_FILES = $(FV_0001_FILES) xxxx-yyy.dxe.
872   // This can then be used as a dependency in their makefile.
873   // Also if they wanted us to dump a cross-reference, do that now.
874   //
875   if (mXRefFileName != NULL) {
876     if ((XRefFptr = fopen (mXRefFileName, "w")) == NULL) {
877       Message (
878         0,
879         "Failed to open cross-reference file '%s' for writing\n",
880         mXRefFileName
881         );
882     }
883   } else {
884     XRefFptr = NULL;
885   }
886 
887   for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) {
888     //
889     // Parse all the "FV1,FV2..." in the FV field that came from FV=FVa,FVb,... on the
890     // component line in the DSC file.
891     //
892     if (FileListPtr->FVs != NULL) {
893       //
894       // If generating a cross-reference file, dump the data
895       //
896       if (XRefFptr != NULL) {
897         if ((FileListPtr->Guid != NULL) && (FileListPtr->BaseName != NULL) && (FileListPtr->Processor)) {
898           fprintf (
899             XRefFptr,
900             "%s %s %s\n",
901             FileListPtr->Guid,
902             FileListPtr->BaseName,
903             FileListPtr->Processor
904             );
905         }
906       }
907       //
908       // Convert to uppercase since we're going to use the name as a macro variable name
909       // in the makefile.
910       //
911       UpperCaseString (FileListPtr->FVs);
912       //
913       // Process each FV this file is in to write fvxxx_FILES = $(fvxxx_FILES) Guid-BaseName.ffs
914       //
915       StartCptr = FileListPtr->FVs;
916       while (*StartCptr) {
917         EndCptr = StartCptr;
918         while (*EndCptr && (*EndCptr != ',')) {
919           EndCptr++;
920         }
921 
922         CSave     = *EndCptr;
923         *EndCptr  = 0;
924         fprintf (
925           MakeFptr,
926           "%s_FILES = $(%s_FILES) %s\n",
927           StartCptr,
928           StartCptr,
929           FileListPtr->FileName
930           );
931         //
932         // Next FV on the FV list
933         //
934         *EndCptr  = CSave;
935         StartCptr = EndCptr;
936         if (*StartCptr) {
937           StartCptr++;
938         }
939       }
940     }
941   }
942 
943   fprintf (MakeFptr, "\n");
944 
945   //
946   // Now go through the list of all NonFFS FVs they specified and search for
947   // a [build.fv.$(FV)] or [build.fv] command and emit the commands to the
948   // output makefile. Add them to the "fvs_0" target as well.
949   //
950   if (mNonFfsFVList != NULL) {
951     fprintf (MakeFptr, "fvs_0 ::");
952     FVPtr = mNonFfsFVList;
953     while (FVPtr != NULL) {
954       fprintf (MakeFptr, " %s%s.fv", FVDir, FVPtr->FVFileName);
955       FVPtr = FVPtr->Next;
956     }
957 
958     fprintf (MakeFptr, "\n\n");
959     FVPtr = mNonFfsFVList;
960     while (FVPtr != NULL) {
961       //
962       // Save the position in the file
963       //
964       DSCFileSavePosition (DSC);
965       //
966       // first try to find a build section specific for this fv.
967       //
968       sprintf (Str, "build.fv.%s", FVPtr->FVFileName);
969       Section = DSCFileFindSection (DSC, Str);
970       if (Section == NULL) {
971         sprintf (Str, "build.fv");
972         Section = DSCFileFindSection (DSC, Str);
973       }
974 
975       if (Section == NULL) {
976         Warning (
977           NULL,
978           0,
979           0,
980           NULL,
981           "No [build.fv.%s] nor [%s] section found in description file for building %s",
982           FVPtr->FVFileName,
983           Str,
984           FVPtr->FVFileName
985           );
986       } else {
987         //
988         // Add a symbol for the FV filename
989         //
990         UpperCaseString (FVPtr->FVFileName);
991         AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE);
992         AddSymbol (EFI_BASE_ADDRESS, FVPtr->BaseAddress, SYM_LOCAL | SYM_OVERWRITE);
993 
994         //
995         // Now copy the build commands from the section to the makefile
996         //
997         while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
998           ExpandSymbols (
999             Line,
1000             ExpandedLine,
1001             sizeof (ExpandedLine),
1002             EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
1003             );
1004 
1005           fprintf (MakeFptr, ExpandedLine);
1006         }
1007       }
1008 
1009       FVPtr = FVPtr->Next;
1010       DSCFileRestorePosition (DSC);
1011     }
1012   }
1013 
1014   //
1015   // Get the components count
1016   //
1017   ComponentCount = -1;
1018   for (FileListPtr = mFileList; FileListPtr != NULL; FileListPtr = FileListPtr->Next) {
1019     if (FileListPtr->ComponentsInstance > ComponentCount) {
1020       ComponentCount = FileListPtr->ComponentsInstance;
1021     }
1022   }
1023   ComponentCount++;
1024 
1025   //
1026   // Now print firmware volumes build targets fvs_0, fvs_1 etc.
1027   //
1028   for (ComponentsInstance = 0; ComponentsInstance < ComponentCount; ComponentsInstance++) {
1029     fprintf (MakeFptr, "fvs_%d ::", ComponentsInstance);
1030     for (FVPtr = mFVList; FVPtr != NULL; FVPtr = FVPtr->Next) {
1031       if (FVPtr->ComponentsInstance == ComponentsInstance) {
1032         fprintf (MakeFptr, " %s%s.fv", FVDir, FVPtr->FVFileName);
1033       }
1034     }
1035     fprintf (MakeFptr, "\n\n");
1036   }
1037 
1038   //
1039   // Create an "fvs" target that builds everything. It has to be a mix of
1040   // components and FV's in order. For example:
1041   // fvs :: components_0 fvs_0 components_1 fvs_1
1042   //
1043   fprintf (MakeFptr, "fvs ::");
1044   for (ComponentsInstance = 0; ComponentsInstance < ComponentCount; ComponentsInstance++) {
1045     fprintf (MakeFptr, " components_%d fvs_%d", ComponentsInstance, ComponentsInstance);
1046   }
1047   fprintf (MakeFptr, "\n\n");
1048 
1049   //
1050   // Create a "components" target for build convenience. It should
1051   // look something like:
1052   // components : components_0 components_1...
1053   //
1054   if (ComponentCount > 0) {
1055     fprintf (MakeFptr, "components :");
1056     for (ComponentsInstance = 0; ComponentsInstance < ComponentCount; ComponentsInstance++) {
1057       fprintf (MakeFptr, " components_%d", ComponentsInstance);
1058     }
1059 
1060     fprintf (MakeFptr, "\n\n");
1061   }
1062   //
1063   // Now go through the list of all FV's defined and search for
1064   // a [build.fv.$(FV)] or [build.fv] command and emit the commands to the
1065   // output makefile.
1066   //
1067   FVPtr = mFVList;
1068   while (FVPtr != NULL) {
1069     if (FVPtr->FVFileName[0]) {
1070       //
1071       // Save the position in the file
1072       //
1073       DSCFileSavePosition (DSC);
1074       //
1075       // First try to find a build section specific for this FV.
1076       //
1077       sprintf (Str, "build.fv.%s", FVPtr->FVFileName);
1078       Section = DSCFileFindSection (DSC, Str);
1079       if (Section == NULL) {
1080         sprintf (Str, "build.fv");
1081         Section = DSCFileFindSection (DSC, Str);
1082       }
1083 
1084       if (Section == NULL) {
1085         Error (
1086           NULL,
1087           0,
1088           0,
1089           NULL,
1090           "no [build.fv.%s] nor [%s] section found in description file for building %s",
1091           FVPtr->FVFileName,
1092           Str,
1093           FVPtr->FVFileName
1094           );
1095       } else {
1096         //
1097         // Add a symbol for the FV filename
1098         //
1099         UpperCaseString (FVPtr->FVFileName);
1100         AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE);
1101         AddSymbol (EFI_BASE_ADDRESS, FVPtr->BaseAddress, SYM_LOCAL | SYM_OVERWRITE);
1102 
1103         //
1104         // Now copy the build commands from the section to the makefile
1105         //
1106         while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
1107           ExpandSymbols (
1108             Line,
1109             ExpandedLine,
1110             sizeof (ExpandedLine),
1111             EXPANDMODE_NO_DESTDIR | EXPANDMODE_NO_SOURCEDIR
1112             );
1113           fprintf (MakeFptr, ExpandedLine);
1114         }
1115       }
1116 
1117       DSCFileRestorePosition (DSC);
1118     }
1119 
1120     FVPtr = FVPtr->Next;
1121   }
1122   //
1123   // Close all the files and free up the memory
1124   //
1125   while (FVList != NULL) {
1126     FVPtr = FVList->Next;
1127     if (FVList->FVFilePtr != NULL) {
1128       SmartClose (FVList->FVFilePtr);
1129     }
1130 
1131     if (FVList->AprioriFilePtr != NULL) {
1132       SmartClose (FVList->AprioriFilePtr);
1133     }
1134 
1135     free (FVList);
1136     FVList = FVPtr;
1137   }
1138 
1139   while (mNonFfsFVList != NULL) {
1140     FVPtr = mNonFfsFVList->Next;
1141     free (mNonFfsFVList);
1142     mNonFfsFVList = FVPtr;
1143   }
1144 
1145   if (XRefFptr != NULL) {
1146     fclose (XRefFptr);
1147   }
1148 
1149   return STATUS_SUCCESS;
1150 }
1151 
1152 int
NonFFSFVWriteInfFiles(DSC_FILE * DSC,char * FileName)1153 NonFFSFVWriteInfFiles (
1154   DSC_FILE  *DSC,
1155   char      *FileName
1156   )
1157 /*++
1158 
1159 Routine Description:
1160 
1161   Generate a Non FFS fv file. It can only some variables,
1162   or simply contains nothing except header.
1163 
1164 Arguments:
1165 
1166   DSC       - pointer to a DSC_FILE object to extract info from
1167   FileName  - pointer to the fv file
1168 
1169 Returns:
1170 
1171   STATUS_SUCCESS  if successful
1172   non-STATUS_SUCCESS  otherwise
1173 
1174 --*/
1175 {
1176   FV_LIST *FVPtr;
1177   SECTION *Section;
1178   char    *StartCptr;
1179   char    *EndCptr;
1180   char    CSave;
1181   char    Str[MAX_PATH];
1182   char    Line[MAX_LINE_LEN];
1183   char    ExpandedLine[MAX_LINE_LEN];
1184   char    FVDir[MAX_PATH];
1185 
1186   //
1187   // See if they specified a FV directory to dump the FV files out to. If not,
1188   // then use the default. Then create the output directory.
1189   //
1190   DSCFileSavePosition (DSC);
1191   StartCptr = GetSymbolValue (FV_INF_DIR);
1192   if (StartCptr == NULL) {
1193     ExpandSymbols (DEFAULT_FV_INF_DIR, FVDir, sizeof (FVDir), EXPANDMODE_NO_UNDEFS);
1194   } else {
1195     strcpy (FVDir, StartCptr);
1196   }
1197 
1198   //
1199   // Make sure the fv directory path ends in /
1200   //
1201   CSave = FVDir[strlen (FVDir) - 1];
1202   if ((CSave != '\\') && (CSave != '/')) {
1203     strcat (FVDir, "\\");
1204   }
1205 
1206   StartCptr = FileName;
1207   while (*StartCptr) {
1208     EndCptr = StartCptr;
1209     while (*EndCptr && (*EndCptr != ',')) {
1210       EndCptr++;
1211     }
1212 
1213     CSave     = *EndCptr;
1214     *EndCptr  = 0;
1215     //
1216     // Ok, we have a fv name, now see if we've already opened
1217     // an fv output file of this name.
1218     //
1219     for (FVPtr = mNonFfsFVList; FVPtr != NULL; FVPtr = FVPtr->Next) {
1220       if (_stricmp (FVPtr->FVFileName, StartCptr) == 0) {
1221         break;
1222       }
1223     }
1224     //
1225     // If there is already one with the same name, wrong
1226     //
1227     if (FVPtr != NULL) {
1228       DSCFileRestorePosition (DSC);
1229       return STATUS_ERROR;
1230     }
1231     //
1232     // Create a new one, add it to the list
1233     //
1234     FVPtr = (FV_LIST *) malloc (sizeof (FV_LIST));
1235     if (FVPtr == NULL) {
1236       Error (__FILE__, __LINE__, 0, "failed to allocate memory", NULL);
1237       DSCFileRestorePosition (DSC);
1238       return STATUS_ERROR;
1239     }
1240 
1241     memset ((char *) FVPtr, 0, sizeof (FV_LIST));
1242     FVPtr->Next   = mNonFfsFVList;
1243     mNonFfsFVList = FVPtr;
1244     //
1245     // Save the FV name in the FileName pointer so we can compare
1246     // for any future FV names specified.
1247     //
1248     strcpy (FVPtr->FVFileName, StartCptr);
1249     //
1250     // Add a symbol for the FV filename
1251     //
1252     UpperCaseString (FVPtr->FVFileName);
1253     AddSymbol (FV_FILENAME, FVPtr->FVFileName, SYM_LOCAL | SYM_OVERWRITE);
1254 
1255     //
1256     // Now create the FVx.inf filename from the fv name and
1257     // default filename extension. Dump it in the FV directory
1258     // as well.
1259     //
1260     strcpy (Str, FVDir);
1261     strcat (Str, FVPtr->FVFileName);
1262     strcat (Str, ".inf");
1263     //
1264     // Create the directory path for our new fv.inf output file.
1265     //
1266     MakeFilePath (Str);
1267     if ((FVPtr->FVFilePtr = SmartOpen (Str)) == NULL) {
1268       Error (NULL, 0, 0, Str, "could not open FV output file");
1269       DSCFileRestorePosition (DSC);
1270       return STATUS_ERROR;
1271     }
1272     //
1273     // Now copy the [fv.fvfile.options] to the fv file
1274     //
1275     sprintf (Str, "fv.%s.options", StartCptr);
1276     Section = DSCFileFindSection (DSC, Str);
1277     if (Section != NULL) {
1278       SmartWrite (FVPtr->FVFilePtr, "[options]\n");
1279       while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
1280         ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0);
1281         SmartWrite (FVPtr->FVFilePtr, ExpandedLine);
1282         GetBaseAddress (ExpandedLine, FVPtr->BaseAddress);
1283       }
1284     } else {
1285       Warning (NULL, 0, 0, NULL, "Could not find FV section '%s' in description file", Str);
1286     }
1287     //
1288     // Copy the [fv.fvfile.attributes] to the fv file
1289     //
1290     sprintf (Str, "fv.%s.attributes", StartCptr);
1291     Section = DSCFileFindSection (DSC, Str);
1292     if (Section != NULL) {
1293       SmartWrite (FVPtr->FVFilePtr, "[attributes]\n");
1294       while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
1295         ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0);
1296         SmartWrite (FVPtr->FVFilePtr, ExpandedLine);
1297       }
1298     } else {
1299       Warning (NULL, 0, 0, NULL, "Could not find FV section '%s' in description file", Str);
1300     }
1301     //
1302     // Copy the [fv.fvfile.components] to the fv file
1303     //
1304     sprintf (Str, "fv.%s.components", StartCptr);
1305     Section = DSCFileFindSection (DSC, Str);
1306     if (Section != NULL) {
1307       SmartWrite (FVPtr->FVFilePtr, "[components]\n");
1308       while (DSCFileGetLine (DSC, Line, sizeof (Line)) != NULL) {
1309         ExpandSymbols (Line, ExpandedLine, sizeof (ExpandedLine), 0);
1310         SmartWrite (FVPtr->FVFilePtr, ExpandedLine);
1311       }
1312     } else {
1313       //
1314       // An empty FV is allowed to contain nothing
1315       //
1316     }
1317     //
1318     // Close the file
1319     //
1320     SmartClose (FVPtr->FVFilePtr);
1321     //
1322     // Next FV in FileName
1323     //
1324     *EndCptr  = CSave;
1325     StartCptr = EndCptr;
1326     if (*StartCptr) {
1327       StartCptr++;
1328     }
1329   }
1330 
1331   DSCFileRestorePosition (DSC);
1332   return STATUS_SUCCESS;
1333 }
1334 
1335 static
1336 void
AddFirmwareVolumes(char * FVs,int ComponentsInstance)1337 AddFirmwareVolumes (
1338   char          *FVs,
1339   int           ComponentsInstance
1340   )
1341 {
1342   FV_LIST *FvPtr;
1343   char    *StartPtr;
1344   char    *EndPtr;
1345   char    SaveChar;
1346 
1347   if ((FVs != NULL) && (FVs[0] != 0)) {
1348     //
1349     // Extract each FV name from the string. It's from the DSC file "FV=FvRecover,FvMain"
1350     //
1351     StartPtr = FVs;
1352     while (*StartPtr != 0) {
1353       EndPtr = StartPtr;
1354       while (*EndPtr && (*EndPtr != ',')) {
1355         EndPtr++;
1356       }
1357 
1358       SaveChar  = *EndPtr;
1359       *EndPtr   = 0;
1360       //
1361       // Look through our list of known firmware volumes and see if we've
1362       // already added it.
1363       //
1364       for (FvPtr = mFVList; FvPtr != NULL; FvPtr = FvPtr->Next) {
1365         if (_stricmp (FvPtr->FVFileName, StartPtr) == 0) {
1366           break;
1367         }
1368       }
1369       //
1370       // If we didn't find a match, then create a new one
1371       //
1372       if (FvPtr == NULL) {
1373         FvPtr = malloc (sizeof (FV_LIST));
1374         if (FvPtr == NULL) {
1375           Error (__FILE__, __LINE__, 0, "application error", "memory allocation failed");
1376           return ;
1377         }
1378 
1379         memset (FvPtr, 0, sizeof (FV_LIST));
1380         strcpy (FvPtr->FVFileName, StartPtr);
1381         if (mFVList == NULL) {
1382           mFVList = FvPtr;
1383         } else {
1384           mFVListLast->Next = FvPtr;
1385         }
1386 
1387         mFVListLast = FvPtr;
1388       }
1389       //
1390       // If this component's section number is higher than that of this
1391       // FV, then set the FV's to it.
1392       //
1393       if (FvPtr->ComponentsInstance < ComponentsInstance) {
1394         FvPtr->ComponentsInstance = ComponentsInstance;
1395       }
1396       //
1397       // If we found then end of the FVs in the string, then we're done.
1398       // Always restore the original string's contents.
1399       //
1400       if (SaveChar != 0) {
1401         *EndPtr   = SaveChar;
1402         StartPtr  = EndPtr + 1;
1403       } else {
1404         StartPtr = EndPtr;
1405       }
1406     }
1407   }
1408 }
1409 
1410 static
1411 BOOLEAN
OrderInFvList(char * FvList,char * FvName,int * Order)1412 OrderInFvList (
1413   char    *FvList,
1414   char    *FvName,
1415   int     *Order
1416   )
1417 {
1418   //
1419   // Given FvList of format "FV_a,FV_b,FV_c" or "FV_a:1,FV_b:2" and
1420   // FvName of format "FV_c", determine if FvName is in FvList. If
1421   // FV_a:1 format, then return the value after the colon.
1422   //
1423   while (*FvList) {
1424     //
1425     // If it matches for the length of FvName...
1426     //
1427     if (_strnicmp (FvList, FvName, strlen (FvName)) == 0) {
1428       //
1429       // Then see if the match string in FvList is terminated at the
1430       // same length.
1431       //
1432       if ((FvList[strlen (FvName)] == ',') || (FvList[strlen (FvName)] == 0)) {
1433         *Order = 0;
1434         return TRUE;
1435       } else if (FvList[strlen (FvName)] == ':') {
1436         *Order = atoi (FvList + strlen (FvName) + 1);
1437         return TRUE;
1438       }
1439     }
1440     //
1441     // Skip to next FV in the comma-separated list
1442     //
1443     while ((*FvList != ',') && (*FvList != 0)) {
1444       FvList++;
1445     }
1446     //
1447     // Skip over comma
1448     //
1449     if (*FvList == ',') {
1450       FvList++;
1451     }
1452   }
1453 
1454   return FALSE;
1455 }
1456 
1457 static
1458 char *
UpperCaseString(char * Str)1459 UpperCaseString (
1460   char *Str
1461   )
1462 {
1463   char  *Cptr;
1464 
1465   for (Cptr = Str; *Cptr; Cptr++) {
1466     *Cptr = (char) toupper (*Cptr);
1467   }
1468 
1469   return Str;
1470 }
1471 
1472 static
1473 BOOLEAN
InSameFv(char * FVs1,char * FVs2)1474 InSameFv (
1475   char  *FVs1,
1476   char  *FVs2
1477 )
1478 {
1479   char    *StartCptr1;
1480   char    *StartCptr2;
1481   char    *EndCptr1;
1482   char    *EndCptr2;
1483   char    CSave1;
1484   char    CSave2;
1485 
1486   //
1487   // Process each FV in first FV list
1488   //
1489   StartCptr1 = FVs1;
1490   while (*StartCptr1) {
1491     EndCptr1 = StartCptr1;
1492     while (*EndCptr1 && (*EndCptr1 != ',')) {
1493       EndCptr1++;
1494     }
1495 
1496     CSave1     = *EndCptr1;
1497     *EndCptr1  = 0;
1498 
1499     if (*StartCptr1) {
1500       //
1501       // Process each FV in second FV list
1502       //
1503       StartCptr2 = FVs2;
1504       while (*StartCptr2) {
1505         EndCptr2 = StartCptr2;
1506         while (*EndCptr2 && (*EndCptr2 != ',')) {
1507           EndCptr2++;
1508         }
1509 
1510         CSave2     = *EndCptr2;
1511         *EndCptr2  = 0;
1512 
1513         if (_stricmp (StartCptr1, StartCptr2) == 0) {
1514           *EndCptr1  = CSave1;
1515           *EndCptr2  = CSave2;
1516           return TRUE;
1517         }
1518 
1519         //
1520         // Next FV on the second FV list
1521         //
1522         *EndCptr2  = CSave2;
1523         StartCptr2 = EndCptr2;
1524         if (*StartCptr2) {
1525           StartCptr2++;
1526         }
1527       }
1528     }
1529 
1530     //
1531     // Next FV on the first FV list
1532     //
1533     *EndCptr1  = CSave1;
1534     StartCptr1 = EndCptr1;
1535     if (*StartCptr1) {
1536       StartCptr1++;
1537     }
1538   }
1539 
1540   return FALSE;
1541 }
1542 
1543 int
CFVSetXRefFileName(char * FileName)1544 CFVSetXRefFileName (
1545   char    *FileName
1546   )
1547 {
1548   mXRefFileName = FileName;
1549   return 0;
1550 }
1551