]> git.proxmox.com Git - mirror_edk2.git/blob - EdkCompatibilityPkg/Sample/Tools/Source/ProcessDsc/FWVolume.c
5181373a54037235ba4d3c869d3e23376a82b11a
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / ProcessDsc / FWVolume.c
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
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
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
500 CFVConstructor (
501 VOID
502 )
503 {
504 mFileList = NULL;
505 mLastFile = NULL;
506 }
507
508 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
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
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
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
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
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 *
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
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
1544 CFVSetXRefFileName (
1545 char *FileName
1546 )
1547 {
1548 mXRefFileName = FileName;
1549 return 0;
1550 }