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 } 1551