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