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 StrGather.c 15 16 Abstract: 17 18 Parse a strings file and create or add to a string database file. 19 20 --*/ 21 22 #include <stdio.h> 23 #include <string.h> 24 #include <stdlib.h> 25 #include <ctype.h> 26 #include <Tiano.h> 27 #include <EfiUtilityMsgs.h> 28 #include <EfiHii.h> 29 #include "StrGather.h" 30 #include "StringDB.h" 31 32 #define UTILITY_NAME "StrGather" 33 #define UTILITY_VERSION "v1.2" 34 35 typedef UINT16 WCHAR; 36 37 #define MAX_PATH 1024 38 #define MAX_NEST_DEPTH 20 // just in case we get in an endless loop. 39 #define MAX_STRING_IDENTIFIER_NAME 128 // number of wchars 40 #define MAX_LINE_LEN 400 41 #define STRING_TOKEN "STRING_TOKEN" 42 #define DEFAULT_BASE_NAME "BaseName" 43 // 44 // Operational modes for this utility 45 // 46 #define MODE_UNKNOWN 0 47 #define MODE_PARSE 1 48 #define MODE_SCAN 2 49 #define MODE_DUMP 3 50 51 // 52 // This is how we invoke the C preprocessor on the source file 53 // to resolve #if, #else, etc. 54 // 55 #define PREPROCESSOR_COMMAND "cl" 56 #define PREPROCESSOR_OPTIONS "/nologo /EP /TC /DSTRGATHER" 57 #define PREPROCESS_TEMP_FILE_EXTENSION ".ii" 58 #define PREPROCESS_OUTPUT_FILE_EXTENSION ".iii" 59 60 // 61 // We keep a linked list of these for the source files we process 62 // 63 typedef struct _SOURCE_FILE { 64 FILE *Fptr; 65 WCHAR *FileBuffer; 66 WCHAR *FileBufferPtr; 67 UINT32 FileSize; 68 INT8 FileName[MAX_PATH]; 69 UINT32 LineNum; 70 BOOLEAN EndOfFile; 71 BOOLEAN SkipToHash; 72 struct _SOURCE_FILE *Previous; 73 struct _SOURCE_FILE *Next; 74 WCHAR ControlCharacter; 75 } SOURCE_FILE; 76 77 #define DEFAULT_CONTROL_CHARACTER UNICODE_SLASH 78 79 // 80 // Here's all our globals. We need a linked list of include paths, a linked 81 // list of source files, a linked list of subdirectories (appended to each 82 // include path when searching), and a couple other fields. 83 // 84 static struct { 85 SOURCE_FILE SourceFiles; 86 TEXT_STRING_LIST *IncludePaths; // all include paths to search 87 TEXT_STRING_LIST *LastIncludePath; 88 TEXT_STRING_LIST *ScanFileName; 89 TEXT_STRING_LIST *LastScanFileName; 90 TEXT_STRING_LIST *SkipExt; // if -skipext .uni 91 TEXT_STRING_LIST *LastSkipExt; 92 TEXT_STRING_LIST *IndirectionFileName; 93 TEXT_STRING_LIST *LastIndirectionFileName; 94 TEXT_STRING_LIST *DatabaseFileName; 95 TEXT_STRING_LIST *LastDatabaseFileName; 96 TEXT_STRING_LIST *PreprocessFlags; 97 TEXT_STRING_LIST *LastPreprocessFlags; 98 WCHAR_STRING_LIST *Language; 99 WCHAR_STRING_LIST *LastLanguage; 100 WCHAR_MATCHING_STRING_LIST *IndirectionList; // from indirection file(s) 101 WCHAR_MATCHING_STRING_LIST *LastIndirectionList; 102 BOOLEAN Verbose; // for more detailed output 103 BOOLEAN VerboseDatabaseWrite; // for more detailed output when writing database 104 BOOLEAN VerboseDatabaseRead; // for more detailed output when reading database 105 BOOLEAN NewDatabase; // to start from scratch 106 BOOLEAN IgnoreNotFound; // when scanning 107 BOOLEAN VerboseScan; 108 BOOLEAN UnquotedStrings; // -uqs option 109 BOOLEAN Preprocess; // -ppflag option 110 INT8 PreprocessFileName[MAX_PATH]; 111 INT8 OutputDatabaseFileName[MAX_PATH]; 112 INT8 StringHFileName[MAX_PATH]; 113 INT8 StringCFileName[MAX_PATH]; // output .C filename 114 INT8 DumpUFileName[MAX_PATH]; // output unicode dump file name 115 INT8 HiiExportPackFileName[MAX_PATH]; // HII export pack file name 116 INT8 BaseName[MAX_PATH]; // base filename of the strings file 117 INT8 OutputDependencyFileName[MAX_PATH]; 118 FILE *OutputDependencyFptr; 119 UINT32 Mode; 120 } mGlobals; 121 122 static 123 BOOLEAN 124 IsValidIdentifierChar ( 125 INT8 Char, 126 BOOLEAN FirstChar 127 ); 128 129 static 130 void 131 RewindFile ( 132 SOURCE_FILE *SourceFile 133 ); 134 135 static 136 BOOLEAN 137 SkipTo ( 138 SOURCE_FILE *SourceFile, 139 WCHAR WChar, 140 BOOLEAN StopAfterNewline 141 ); 142 143 static 144 UINT32 145 SkipWhiteSpace ( 146 SOURCE_FILE *SourceFile 147 ); 148 149 static 150 BOOLEAN 151 IsWhiteSpace ( 152 SOURCE_FILE *SourceFile 153 ); 154 155 static 156 BOOLEAN 157 EndOfFile ( 158 SOURCE_FILE *SourceFile 159 ); 160 161 static 162 void 163 PreprocessFile ( 164 SOURCE_FILE *SourceFile 165 ); 166 167 static 168 UINT32 169 GetStringIdentifierName ( 170 IN SOURCE_FILE *SourceFile, 171 IN OUT WCHAR *StringIdentifierName, 172 IN UINT32 StringIdentifierNameLen 173 ); 174 175 static 176 STATUS 177 GetLanguageIdentifierName ( 178 IN SOURCE_FILE *SourceFile, 179 IN OUT WCHAR *LanguageIdentifierName, 180 IN UINT32 LanguageIdentifierNameLen, 181 IN BOOLEAN Optional 182 ); 183 184 static 185 WCHAR * 186 GetPrintableLanguageName ( 187 IN SOURCE_FILE *SourceFile 188 ); 189 190 static 191 STATUS 192 AddCommandLineLanguage ( 193 IN INT8 *Language 194 ); 195 196 static 197 WCHAR * 198 GetQuotedString ( 199 SOURCE_FILE *SourceFile, 200 BOOLEAN Optional 201 ); 202 203 static 204 STATUS 205 ProcessIncludeFile ( 206 SOURCE_FILE *SourceFile, 207 SOURCE_FILE *ParentSourceFile 208 ); 209 210 static 211 STATUS 212 ParseFile ( 213 SOURCE_FILE *SourceFile 214 ); 215 216 static 217 FILE * 218 FindFile ( 219 IN INT8 *FileName, 220 OUT INT8 *FoundFileName, 221 IN UINT32 FoundFileNameLen 222 ); 223 224 static 225 STATUS 226 ProcessArgs ( 227 int Argc, 228 char *Argv[] 229 ); 230 231 static 232 STATUS 233 ProcessFile ( 234 SOURCE_FILE *SourceFile 235 ); 236 237 static 238 UINT32 239 wstrcmp ( 240 WCHAR *Buffer, 241 WCHAR *Str 242 ); 243 244 static 245 WCHAR * 246 wstrcatenate ( 247 WCHAR *Dst, 248 WCHAR *Src 249 ); 250 251 static 252 void 253 Usage ( 254 VOID 255 ); 256 257 static 258 void 259 FreeLists ( 260 VOID 261 ); 262 263 static 264 void 265 ProcessTokenString ( 266 SOURCE_FILE *SourceFile 267 ); 268 269 static 270 void 271 ProcessTokenInclude ( 272 SOURCE_FILE *SourceFile 273 ); 274 275 static 276 void 277 ProcessTokenScope ( 278 SOURCE_FILE *SourceFile 279 ); 280 281 static 282 void 283 ProcessTokenLanguage ( 284 SOURCE_FILE *SourceFile 285 ); 286 287 static 288 void 289 ProcessTokenLangDef ( 290 SOURCE_FILE *SourceFile 291 ); 292 293 static 294 VOID 295 ProcessTokenSecondaryLangDef ( 296 SOURCE_FILE *SourceFile 297 ); 298 299 static 300 STATUS 301 ScanFiles ( 302 TEXT_STRING_LIST *ScanFiles 303 ); 304 305 static 306 STATUS 307 ParseIndirectionFiles ( 308 TEXT_STRING_LIST *Files 309 ); 310 311 int 312 main ( 313 int Argc, 314 char *Argv[] 315 ) 316 /*++ 317 318 Routine Description: 319 320 Call the routine to parse the command-line options, then process the file. 321 322 Arguments: 323 324 Argc - Standard C main() argc and argv. 325 Argv - Standard C main() argc and argv. 326 327 Returns: 328 329 0 if successful 330 nonzero otherwise 331 332 --*/ 333 { 334 STATUS Status; 335 336 SetUtilityName (UTILITY_NAME); 337 // 338 // Process the command-line arguments 339 // 340 Status = ProcessArgs (Argc, Argv); 341 if (Status != STATUS_SUCCESS) { 342 return Status; 343 } 344 // 345 // Initialize the database manager 346 // 347 StringDBConstructor (); 348 // 349 // We always try to read in an existing database file. It may not 350 // exist, which is ok usually. 351 // 352 if (mGlobals.NewDatabase == 0) { 353 // 354 // Read all databases specified. 355 // 356 for (mGlobals.LastDatabaseFileName = mGlobals.DatabaseFileName; 357 mGlobals.LastDatabaseFileName != NULL; 358 mGlobals.LastDatabaseFileName = mGlobals.LastDatabaseFileName->Next 359 ) { 360 Status = StringDBReadDatabase (mGlobals.LastDatabaseFileName->Str, TRUE, mGlobals.VerboseDatabaseRead); 361 if (Status != STATUS_SUCCESS) { 362 return Status; 363 } 364 } 365 } 366 // 367 // Read indirection file(s) if specified 368 // 369 if (ParseIndirectionFiles (mGlobals.IndirectionFileName) != STATUS_SUCCESS) { 370 goto Finish; 371 } 372 // 373 // If scanning source files, do that now 374 // 375 if (mGlobals.Mode == MODE_SCAN) { 376 ScanFiles (mGlobals.ScanFileName); 377 } else if (mGlobals.Mode == MODE_PARSE) { 378 // 379 // Parsing a unicode strings file 380 // 381 mGlobals.SourceFiles.ControlCharacter = DEFAULT_CONTROL_CHARACTER; 382 if (mGlobals.OutputDependencyFileName[0] != 0) { 383 if ((mGlobals.OutputDependencyFptr = fopen (mGlobals.OutputDependencyFileName, "w")) == NULL) { 384 Error (NULL, 0, 0, mGlobals.OutputDependencyFileName, "failed to open output dependency file"); 385 goto Finish; 386 } 387 } 388 Status = ProcessIncludeFile (&mGlobals.SourceFiles, NULL); 389 if (mGlobals.OutputDependencyFptr != NULL) { 390 fclose (mGlobals.OutputDependencyFptr); 391 } 392 if (Status != STATUS_SUCCESS) { 393 goto Finish; 394 } 395 } 396 // 397 // Create the string defines header file if there have been no errors. 398 // 399 ParserSetPosition (NULL, 0); 400 if ((mGlobals.StringHFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { 401 Status = StringDBDumpStringDefines (mGlobals.StringHFileName, mGlobals.BaseName); 402 if (Status != EFI_SUCCESS) { 403 goto Finish; 404 } 405 } 406 407 // 408 // Dump the strings to a .c file if there have still been no errors. 409 // 410 if ((mGlobals.StringCFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { 411 Status = StringDBDumpCStrings ( 412 mGlobals.BaseName, 413 mGlobals.StringCFileName, 414 mGlobals.Language 415 ); 416 if (Status != EFI_SUCCESS) { 417 goto Finish; 418 } 419 } 420 421 // 422 // Dump the database if requested 423 // 424 if ((mGlobals.DumpUFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { 425 StringDBDumpDatabase (NULL, mGlobals.DumpUFileName, FALSE); 426 } 427 // 428 // Dump the string data as HII binary string pack if requested 429 // 430 if ((mGlobals.HiiExportPackFileName[0] != 0) && (GetUtilityStatus () < STATUS_ERROR)) { 431 StringDBCreateHiiExportPack (mGlobals.HiiExportPackFileName, mGlobals.Language); 432 } 433 // 434 // Always update the database if no errors and not in dump mode. If they specified -od 435 // for an output database file name, then use that name. Otherwise use the name of 436 // the first database file specified with -db 437 // 438 if ((mGlobals.Mode != MODE_DUMP) && (GetUtilityStatus () < STATUS_ERROR)) { 439 if (mGlobals.OutputDatabaseFileName[0]) { 440 Status = StringDBWriteDatabase (mGlobals.OutputDatabaseFileName, mGlobals.VerboseDatabaseWrite); 441 } else { 442 Status = StringDBWriteDatabase (mGlobals.DatabaseFileName->Str, mGlobals.VerboseDatabaseWrite); 443 } 444 445 if (Status != EFI_SUCCESS) { 446 goto Finish; 447 } 448 } 449 450 Finish: 451 // 452 // Free up memory 453 // 454 FreeLists (); 455 StringDBDestructor (); 456 return GetUtilityStatus (); 457 } 458 459 static 460 STATUS 461 ProcessIncludeFile ( 462 SOURCE_FILE *SourceFile, 463 SOURCE_FILE *ParentSourceFile 464 ) 465 /*++ 466 467 Routine Description: 468 469 Given a source file, open the file and parse it 470 471 Arguments: 472 473 SourceFile - name of file to parse 474 ParentSourceFile - for error reporting purposes, the file that #included SourceFile. 475 476 Returns: 477 478 Standard status. 479 480 --*/ 481 { 482 static UINT32 NestDepth = 0; 483 INT8 FoundFileName[MAX_PATH]; 484 STATUS Status; 485 486 Status = STATUS_SUCCESS; 487 NestDepth++; 488 // 489 // Print the file being processed. Indent so you can tell the include nesting 490 // depth. 491 // 492 if (mGlobals.Verbose) { 493 fprintf (stdout, "%*cProcessing file '%s'\n", NestDepth * 2, ' ', SourceFile->FileName); 494 } 495 496 // 497 // Make sure we didn't exceed our maximum nesting depth 498 // 499 if (NestDepth > MAX_NEST_DEPTH) { 500 Error (NULL, 0, 0, SourceFile->FileName, "max nesting depth (%d) exceeded", NestDepth); 501 Status = STATUS_ERROR; 502 goto Finish; 503 } 504 // 505 // Try to open the file locally, and if that fails try along our include paths. 506 // 507 strcpy (FoundFileName, SourceFile->FileName); 508 if ((SourceFile->Fptr = fopen (FoundFileName, "rb")) == NULL) { 509 // 510 // Try to find it among the paths if it has a parent (that is, it is included 511 // by someone else). 512 // 513 if (ParentSourceFile == NULL) { 514 Error (NULL, 0, 0, SourceFile->FileName, "file not found"); 515 Status = STATUS_ERROR; 516 goto Finish; 517 } 518 519 SourceFile->Fptr = FindFile (SourceFile->FileName, FoundFileName, sizeof (FoundFileName)); 520 if (SourceFile->Fptr == NULL) { 521 Error (ParentSourceFile->FileName, ParentSourceFile->LineNum, 0, SourceFile->FileName, "include file not found"); 522 Status = STATUS_ERROR; 523 goto Finish; 524 } 525 } 526 527 // 528 // Output the dependency 529 // 530 if (mGlobals.OutputDependencyFptr != NULL) { 531 fprintf (mGlobals.OutputDependencyFptr, "%s : %s\n", mGlobals.DatabaseFileName->Str, FoundFileName); 532 // 533 // Add pseudo target to avoid incremental build failure when the file is deleted 534 // 535 fprintf (mGlobals.OutputDependencyFptr, "%s : \n", FoundFileName); 536 } 537 538 // 539 // Process the file found 540 // 541 ProcessFile (SourceFile); 542 543 Finish: 544 NestDepth--; 545 // 546 // Close open files and return status 547 // 548 if (SourceFile->Fptr != NULL) { 549 fclose (SourceFile->Fptr); 550 } 551 552 return Status; 553 } 554 555 static 556 STATUS 557 ProcessFile ( 558 SOURCE_FILE *SourceFile 559 ) 560 { 561 // 562 // Get the file size, and then read the entire thing into memory. 563 // Allocate space for a terminator character. 564 // 565 fseek (SourceFile->Fptr, 0, SEEK_END); 566 SourceFile->FileSize = ftell (SourceFile->Fptr); 567 fseek (SourceFile->Fptr, 0, SEEK_SET); 568 SourceFile->FileBuffer = (WCHAR *) malloc (SourceFile->FileSize + sizeof (WCHAR)); 569 if (SourceFile->FileBuffer == NULL) { 570 Error (NULL, 0, 0, "memory allocation failure", NULL); 571 return STATUS_ERROR; 572 } 573 574 fread ((VOID *) SourceFile->FileBuffer, SourceFile->FileSize, 1, SourceFile->Fptr); 575 SourceFile->FileBuffer[(SourceFile->FileSize / sizeof (WCHAR))] = UNICODE_NULL; 576 // 577 // Pre-process the file to replace comments with spaces 578 // 579 PreprocessFile (SourceFile); 580 // 581 // Parse the file 582 // 583 ParseFile (SourceFile); 584 free (SourceFile->FileBuffer); 585 return STATUS_SUCCESS; 586 } 587 588 static 589 STATUS 590 ParseFile ( 591 SOURCE_FILE *SourceFile 592 ) 593 { 594 BOOLEAN InComment; 595 UINT32 Len; 596 597 // 598 // First character of a unicode file is special. Make sure 599 // 600 if (SourceFile->FileBufferPtr[0] != UNICODE_FILE_START) { 601 Error (SourceFile->FileName, 1, 0, SourceFile->FileName, "file does not appear to be a unicode file"); 602 return STATUS_ERROR; 603 } 604 605 SourceFile->FileBufferPtr++; 606 InComment = FALSE; 607 // 608 // Print the first line if in verbose mode 609 // 610 if (mGlobals.Verbose) { 611 printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); 612 } 613 // 614 // Since the syntax is relatively straightforward, just switch on the next char 615 // 616 while (!EndOfFile (SourceFile)) { 617 // 618 // Check for whitespace 619 // 620 if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) { 621 SourceFile->FileBufferPtr++; 622 } else if (SourceFile->FileBufferPtr[0] == UNICODE_TAB) { 623 SourceFile->FileBufferPtr++; 624 } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { 625 SourceFile->FileBufferPtr++; 626 } else if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { 627 SourceFile->FileBufferPtr++; 628 SourceFile->LineNum++; 629 if (mGlobals.Verbose) { 630 printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); 631 } 632 633 InComment = FALSE; 634 } else if (SourceFile->FileBufferPtr[0] == 0) { 635 SourceFile->FileBufferPtr++; 636 } else if (InComment) { 637 SourceFile->FileBufferPtr++; 638 } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) { 639 SourceFile->FileBufferPtr += 2; 640 InComment = TRUE; 641 } else if (SourceFile->SkipToHash && (SourceFile->FileBufferPtr[0] != SourceFile->ControlCharacter)) { 642 SourceFile->FileBufferPtr++; 643 } else { 644 SourceFile->SkipToHash = FALSE; 645 if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 646 ((Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"include")) > 0) 647 ) { 648 SourceFile->FileBufferPtr += Len + 1; 649 ProcessTokenInclude (SourceFile); 650 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 651 (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"scope")) > 0 652 ) { 653 SourceFile->FileBufferPtr += Len + 1; 654 ProcessTokenScope (SourceFile); 655 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 656 (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"language")) > 0 657 ) { 658 SourceFile->FileBufferPtr += Len + 1; 659 ProcessTokenLanguage (SourceFile); 660 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 661 (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"langdef")) > 0 662 ) { 663 SourceFile->FileBufferPtr += Len + 1; 664 ProcessTokenLangDef (SourceFile); 665 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 666 (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"secondarylang")) > 0 667 ) { 668 SourceFile->FileBufferPtr += Len + 1; 669 ProcessTokenSecondaryLangDef (SourceFile); 670 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 671 (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"string")) > 0 672 ) { 673 SourceFile->FileBufferPtr += Len + 1; 674 ProcessTokenString (SourceFile); 675 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 676 (Len = wstrcmp (SourceFile->FileBufferPtr + 1, L"EFI_BREAKPOINT()")) > 0 677 ) { 678 SourceFile->FileBufferPtr += Len; 679 EFI_BREAKPOINT (); 680 } else if ((SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter) && 681 (SourceFile->FileBufferPtr[1] == UNICODE_EQUAL_SIGN) 682 ) { 683 SourceFile->ControlCharacter = SourceFile->FileBufferPtr[2]; 684 SourceFile->FileBufferPtr += 3; 685 } else { 686 Error (SourceFile->FileName, SourceFile->LineNum, 0, "unrecognized token", "%S", SourceFile->FileBufferPtr); 687 // 688 // Treat rest of line as a comment. 689 // 690 InComment = TRUE; 691 } 692 } 693 } 694 695 return STATUS_SUCCESS; 696 } 697 698 static 699 void 700 PreprocessFile ( 701 SOURCE_FILE *SourceFile 702 ) 703 /*++ 704 705 Routine Description: 706 Preprocess a file to replace all carriage returns with NULLs so 707 we can print lines from the file to the screen. 708 709 Arguments: 710 SourceFile - structure that we use to keep track of an input file. 711 712 Returns: 713 Nothing. 714 715 --*/ 716 { 717 BOOLEAN InComment; 718 719 RewindFile (SourceFile); 720 InComment = FALSE; 721 while (!EndOfFile (SourceFile)) { 722 // 723 // If a line-feed, then no longer in a comment 724 // 725 if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { 726 SourceFile->FileBufferPtr++; 727 SourceFile->LineNum++; 728 InComment = 0; 729 } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { 730 // 731 // Replace all carriage returns with a NULL so we can print stuff 732 // 733 SourceFile->FileBufferPtr[0] = 0; 734 SourceFile->FileBufferPtr++; 735 } else if (InComment) { 736 SourceFile->FileBufferPtr[0] = UNICODE_SPACE; 737 SourceFile->FileBufferPtr++; 738 } else if ((SourceFile->FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile->FileBufferPtr[1] == UNICODE_SLASH)) { 739 SourceFile->FileBufferPtr += 2; 740 InComment = TRUE; 741 } else { 742 SourceFile->FileBufferPtr++; 743 } 744 } 745 // 746 // Could check for end-of-file and still in a comment, but 747 // should not be necessary. So just restore the file pointers. 748 // 749 RewindFile (SourceFile); 750 } 751 752 static 753 WCHAR * 754 GetPrintableLanguageName ( 755 IN SOURCE_FILE *SourceFile 756 ) 757 { 758 WCHAR *String; 759 WCHAR *Start; 760 WCHAR *Ptr; 761 UINT32 Len; 762 763 SkipWhiteSpace (SourceFile); 764 if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { 765 Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted printable language name", "%S", SourceFile->FileBufferPtr); 766 SourceFile->SkipToHash = TRUE; 767 return NULL; 768 } 769 770 Len = 0; 771 SourceFile->FileBufferPtr++; 772 Start = Ptr = SourceFile->FileBufferPtr; 773 while (!EndOfFile (SourceFile)) { 774 if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { 775 Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start); 776 break; 777 } else if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) { 778 break; 779 } 780 781 SourceFile->FileBufferPtr++; 782 Len++; 783 } 784 785 if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { 786 Warning ( 787 SourceFile->FileName, 788 SourceFile->LineNum, 789 0, 790 "missing closing quote on printable language name string", 791 "%S", 792 Start 793 ); 794 } else { 795 SourceFile->FileBufferPtr++; 796 } 797 // 798 // Now allocate memory for the string and save it off 799 // 800 String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); 801 if (String == NULL) { 802 Error (NULL, 0, 0, "memory allocation failed", NULL); 803 return NULL; 804 } 805 // 806 // Copy the string from the file buffer to the local copy. 807 // We do no reformatting of it whatsoever at this point. 808 // 809 Ptr = String; 810 while (Len > 0) { 811 *Ptr = *Start; 812 Start++; 813 Ptr++; 814 Len--; 815 } 816 817 *Ptr = 0; 818 // 819 // Now format the string to convert \wide and \narrow controls 820 // 821 StringDBFormatString (String); 822 return String; 823 } 824 825 static struct { 826 WCHAR *ISO639; 827 WCHAR *RFC3066; 828 } LanguageConvertTable[] = { 829 { L"eng", L"en-US" }, 830 { L"fra", L"fr-FR" }, 831 { L"spa", L"es-ES" }, 832 { NULL, NULL } 833 }; 834 835 WCHAR * 836 GetLangCode ( 837 IN WCHAR *Lang 838 ) 839 { 840 UINT32 Index; 841 WCHAR *LangCode; 842 843 LangCode = NULL; 844 845 // 846 // The Lang is xx-XX format and return. 847 // 848 if (wcschr (Lang, L'-') != NULL) { 849 LangCode = (WCHAR *) malloc ((wcslen (Lang) + 1) * sizeof(WCHAR)); 850 if (LangCode != NULL) { 851 wcscpy (LangCode, Lang); 852 } 853 return LangCode; 854 } 855 856 // 857 // Convert the language accoring to the table. 858 // 859 for (Index = 0; LanguageConvertTable[Index].ISO639 != NULL; Index++) { 860 if (wcscmp(LanguageConvertTable[Index].ISO639, Lang) == 0) { 861 LangCode = (WCHAR *) malloc ((wcslen (LanguageConvertTable[Index].RFC3066) + 1) * sizeof (WCHAR)); 862 if (LangCode != NULL) { 863 wcscpy (LangCode, LanguageConvertTable[Index].RFC3066); 864 } 865 return LangCode; 866 } 867 } 868 869 return NULL; 870 } 871 872 WCHAR * 873 GetLangCodeList ( 874 IN WCHAR *SecondaryLangList 875 ) 876 { 877 WCHAR *CodeBeg, *CodeEnd; 878 WCHAR *CodeRet; 879 WCHAR *LangCodeList = NULL; 880 WCHAR *TempLangCodeList = NULL; 881 882 TempLangCodeList = (WCHAR *) malloc ((wcslen(SecondaryLangList) + 1) * sizeof(WCHAR)); 883 if (TempLangCodeList == NULL) { 884 return NULL; 885 } 886 wcscpy (TempLangCodeList, SecondaryLangList); 887 CodeBeg = TempLangCodeList; 888 889 while (CodeBeg != NULL) { 890 CodeEnd = wcschr (CodeBeg, L';'); 891 if (CodeEnd != NULL) { 892 *CodeEnd = L'\0'; 893 CodeEnd++; 894 } 895 896 CodeRet = GetLangCode (CodeBeg); 897 if (CodeRet != NULL) { 898 if (LangCodeList != NULL) { 899 LangCodeList = wstrcatenate (LangCodeList, L";"); 900 } 901 LangCodeList = wstrcatenate (LangCodeList, CodeRet); 902 } 903 904 CodeBeg = CodeEnd; 905 FREE (CodeRet); 906 } 907 908 free (TempLangCodeList); 909 910 return LangCodeList; 911 } 912 913 static 914 WCHAR * 915 GetSecondaryLanguageList ( 916 IN SOURCE_FILE *SourceFile 917 ) 918 { 919 WCHAR *SecondaryLangList = NULL; 920 WCHAR SecondaryLang[MAX_STRING_IDENTIFIER_NAME + 1]; 921 WCHAR *LangCodeList; 922 WCHAR *Start; 923 WCHAR *Ptr; 924 UINT32 Index; 925 926 SkipWhiteSpace (SourceFile); 927 928 if (SourceFile->FileBufferPtr[0] != UNICODE_OPEN_PAREN) { 929 Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected open bracket", "%S", SourceFile->FileBufferPtr); 930 SourceFile->SkipToHash = TRUE; 931 return NULL; 932 } 933 934 Index = 0; 935 SecondaryLang [0] = L'\0'; 936 SourceFile->FileBufferPtr++; 937 Start = Ptr = SourceFile->FileBufferPtr; 938 while (!EndOfFile (SourceFile)) { 939 if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || 940 ((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || 941 (SourceFile->FileBufferPtr[0] == UNICODE_MINUS)) { 942 if (Index > MAX_STRING_IDENTIFIER_NAME) { 943 Error (SourceFile->FileName, SourceFile->LineNum, 0, "secondary language length is too lang", "%S", SourceFile->FileBufferPtr); 944 goto Err; 945 } 946 SecondaryLang[Index] = SourceFile->FileBufferPtr[0]; 947 Index++; 948 } else if (SourceFile->FileBufferPtr[0] == UNICODE_SPACE) { 949 SecondaryLang[Index] = L'\0'; 950 951 if (SecondaryLang[0] != L'\0') { 952 if (SecondaryLangList != NULL) { 953 SecondaryLangList = wstrcatenate (SecondaryLangList, L";"); 954 } 955 SecondaryLangList = wstrcatenate (SecondaryLangList, SecondaryLang); 956 Index = 0; 957 SecondaryLang [0] = L'\0'; 958 SourceFile->FileBufferPtr++; 959 continue; 960 } 961 } else if (SourceFile->FileBufferPtr[0] == UNICODE_CLOSE_PAREN) { 962 if (SecondaryLangList != NULL) { 963 SecondaryLangList = wstrcatenate (SecondaryLangList, L";"); 964 } 965 SecondaryLangList = wstrcatenate (SecondaryLangList, SecondaryLang); 966 break; 967 } else { 968 Error (SourceFile->FileName, SourceFile->LineNum, 0, "can not recognize the secondary language", "%S", SourceFile->FileBufferPtr); 969 goto Err; 970 } 971 972 SourceFile->FileBufferPtr++; 973 } 974 975 if (SourceFile->FileBufferPtr[0] != UNICODE_CLOSE_PAREN) { 976 Error (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing bracket", "%S", Start); 977 } else { 978 SourceFile->FileBufferPtr++; 979 } 980 981 LangCodeList = GetLangCodeList (SecondaryLangList); 982 FREE (SecondaryLangList); 983 return LangCodeList; 984 985 Err: 986 FREE(SecondaryLangList); 987 return NULL; 988 } 989 990 static 991 WCHAR * 992 GetQuotedString ( 993 SOURCE_FILE *SourceFile, 994 BOOLEAN Optional 995 ) 996 { 997 WCHAR *String; 998 WCHAR *Start; 999 WCHAR *Ptr; 1000 UINT32 Len; 1001 BOOLEAN PreviousBackslash; 1002 1003 if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { 1004 if (!Optional) { 1005 Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted string", "%S", SourceFile->FileBufferPtr); 1006 } 1007 1008 return NULL; 1009 } 1010 1011 Len = 0; 1012 SourceFile->FileBufferPtr++; 1013 Start = Ptr = SourceFile->FileBufferPtr; 1014 PreviousBackslash = FALSE; 1015 while (!EndOfFile (SourceFile)) { 1016 if ((SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) && (!PreviousBackslash)) { 1017 break; 1018 } else if (SourceFile->FileBufferPtr[0] == UNICODE_CR) { 1019 Warning (SourceFile->FileName, SourceFile->LineNum, 0, "carriage return found in quoted string", "%S", Start); 1020 PreviousBackslash = FALSE; 1021 } else if (SourceFile->FileBufferPtr[0] == UNICODE_BACKSLASH) { 1022 PreviousBackslash = TRUE; 1023 } else { 1024 PreviousBackslash = FALSE; 1025 } 1026 1027 SourceFile->FileBufferPtr++; 1028 Len++; 1029 } 1030 1031 if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { 1032 Warning (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", "%S", Start); 1033 } else { 1034 SourceFile->FileBufferPtr++; 1035 } 1036 // 1037 // Now allocate memory for the string and save it off 1038 // 1039 String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); 1040 if (String == NULL) { 1041 Error (NULL, 0, 0, "memory allocation failed", NULL); 1042 return NULL; 1043 } 1044 // 1045 // Copy the string from the file buffer to the local copy. 1046 // We do no reformatting of it whatsoever at this point. 1047 // 1048 Ptr = String; 1049 while (Len > 0) { 1050 *Ptr = *Start; 1051 Start++; 1052 Ptr++; 1053 Len--; 1054 } 1055 1056 *Ptr = 0; 1057 return String; 1058 } 1059 // 1060 // Parse: 1061 // #string STR_ID_NAME 1062 // 1063 // All we can do is call the string database to add the string identifier. Unfortunately 1064 // he'll have to keep track of the last identifier we added. 1065 // 1066 static 1067 void 1068 ProcessTokenString ( 1069 SOURCE_FILE *SourceFile 1070 ) 1071 { 1072 WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; 1073 UINT16 StringId; 1074 // 1075 // Extract the string identifier name and add it to the database. 1076 // 1077 if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) { 1078 StringId = STRING_ID_INVALID; 1079 StringDBAddStringIdentifier (StringIdentifier, &StringId, 0); 1080 } else { 1081 // 1082 // Error recovery -- skip to the next # 1083 // 1084 SourceFile->SkipToHash = TRUE; 1085 } 1086 } 1087 1088 static 1089 BOOLEAN 1090 EndOfFile ( 1091 SOURCE_FILE *SourceFile 1092 ) 1093 { 1094 // 1095 // The file buffer pointer will typically get updated before the End-of-file flag in the 1096 // source file structure, so check it first. 1097 // 1098 if (SourceFile->FileBufferPtr >= SourceFile->FileBuffer + SourceFile->FileSize / sizeof (WCHAR)) { 1099 SourceFile->EndOfFile = TRUE; 1100 return TRUE; 1101 } 1102 1103 if (SourceFile->EndOfFile) { 1104 return TRUE; 1105 } 1106 1107 return FALSE; 1108 } 1109 1110 static 1111 UINT32 1112 GetStringIdentifierName ( 1113 IN SOURCE_FILE *SourceFile, 1114 IN OUT WCHAR *StringIdentifierName, 1115 IN UINT32 StringIdentifierNameLen 1116 ) 1117 { 1118 UINT32 Len; 1119 WCHAR *From; 1120 WCHAR *Start; 1121 1122 // 1123 // Skip whitespace 1124 // 1125 SkipWhiteSpace (SourceFile); 1126 if (SourceFile->EndOfFile) { 1127 Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected string identifier"); 1128 return 0; 1129 } 1130 // 1131 // Verify first character of name is [A-Za-z] 1132 // 1133 Len = 0; 1134 StringIdentifierNameLen /= 2; 1135 From = SourceFile->FileBufferPtr; 1136 Start = SourceFile->FileBufferPtr; 1137 if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || 1138 ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) 1139 ) { 1140 // 1141 // Do nothing 1142 // 1143 } else { 1144 Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid character in string identifier name", "%S", Start); 1145 return 0; 1146 } 1147 1148 while (!EndOfFile (SourceFile)) { 1149 if (((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || 1150 ((SourceFile->FileBufferPtr[0] >= UNICODE_z) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || 1151 ((SourceFile->FileBufferPtr[0] >= UNICODE_0) && (SourceFile->FileBufferPtr[0] <= UNICODE_9)) || 1152 (SourceFile->FileBufferPtr[0] == UNICODE_UNDERSCORE) 1153 ) { 1154 Len++; 1155 if (Len >= StringIdentifierNameLen) { 1156 Error (SourceFile->FileName, SourceFile->LineNum, 0, "string identifier name too long", "%S", Start); 1157 return 0; 1158 } 1159 1160 *StringIdentifierName = SourceFile->FileBufferPtr[0]; 1161 StringIdentifierName++; 1162 SourceFile->FileBufferPtr++; 1163 } else if (SkipWhiteSpace (SourceFile) == 0) { 1164 Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid string identifier name", "%S", Start); 1165 return 0; 1166 } else { 1167 break; 1168 } 1169 } 1170 // 1171 // Terminate the copy of the string. 1172 // 1173 *StringIdentifierName = 0; 1174 return Len; 1175 } 1176 1177 static 1178 STATUS 1179 GetLanguageIdentifierName ( 1180 IN SOURCE_FILE *SourceFile, 1181 IN OUT WCHAR *LanguageIdentifierName, 1182 IN UINT32 LanguageIdentifierNameLen, 1183 IN BOOLEAN Optional 1184 ) 1185 { 1186 UINT32 Len; 1187 WCHAR *Start; 1188 WCHAR *LangCode; 1189 WCHAR *LanguageIdentifier; 1190 1191 LanguageIdentifier = LanguageIdentifierName; 1192 1193 // 1194 // Skip whitespace 1195 // 1196 SkipWhiteSpace (SourceFile); 1197 if (SourceFile->EndOfFile) { 1198 if (!Optional) { 1199 Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-file encountered", "expected language identifier"); 1200 return STATUS_ERROR; 1201 } 1202 1203 return STATUS_SUCCESS; 1204 } 1205 // 1206 // This function is called to optionally get a language identifier name in: 1207 // #string STR_ID eng "the string" 1208 // If it's optional, and we find a double-quote, then return now. 1209 // 1210 if (Optional) { 1211 if (*SourceFile->FileBufferPtr == UNICODE_DOUBLE_QUOTE) { 1212 return STATUS_SUCCESS; 1213 } 1214 } 1215 1216 LanguageIdentifierNameLen /= 2; 1217 // 1218 // Internal error if we weren't given at least 4 WCHAR's to work with. 1219 // 1220 if (LanguageIdentifierNameLen < LANGUAGE_IDENTIFIER_NAME_LEN + 1) { 1221 Error ( 1222 SourceFile->FileName, 1223 SourceFile->LineNum, 1224 0, 1225 "app error -- language identifier name length is invalid", 1226 NULL 1227 ); 1228 } 1229 1230 Len = 0; 1231 Start = SourceFile->FileBufferPtr; 1232 while (!EndOfFile (SourceFile)) { 1233 if (((SourceFile->FileBufferPtr[0] >= UNICODE_a) && (SourceFile->FileBufferPtr[0] <= UNICODE_z)) || 1234 ((SourceFile->FileBufferPtr[0] >= UNICODE_A) && (SourceFile->FileBufferPtr[0] <= UNICODE_Z)) || 1235 (SourceFile->FileBufferPtr[0] == UNICODE_MINUS)) { 1236 Len++; 1237 if (Len > LANGUAGE_IDENTIFIER_NAME_LEN) { 1238 Error (SourceFile->FileName, SourceFile->LineNum, 0, "language identifier name too long", "%S", Start); 1239 return STATUS_ERROR; 1240 } 1241 *LanguageIdentifierName = SourceFile->FileBufferPtr[0]; 1242 SourceFile->FileBufferPtr++; 1243 LanguageIdentifierName++; 1244 } else if (!IsWhiteSpace (SourceFile)) { 1245 Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid language identifier name", "%S", Start); 1246 return STATUS_ERROR; 1247 } else { 1248 break; 1249 } 1250 } 1251 // 1252 // Terminate the copy of the string. 1253 // 1254 *LanguageIdentifierName = 0; 1255 LangCode = GetLangCode (LanguageIdentifier); 1256 if (LangCode != NULL) { 1257 wcscpy (LanguageIdentifier, LangCode); 1258 FREE (LangCode); 1259 } 1260 return STATUS_SUCCESS; 1261 } 1262 1263 static 1264 void 1265 ProcessTokenInclude ( 1266 SOURCE_FILE *SourceFile 1267 ) 1268 { 1269 INT8 IncludeFileName[MAX_PATH]; 1270 INT8 *To; 1271 UINT32 Len; 1272 BOOLEAN ReportedError; 1273 SOURCE_FILE IncludedSourceFile; 1274 1275 ReportedError = FALSE; 1276 if (SkipWhiteSpace (SourceFile) == 0) { 1277 Warning (SourceFile->FileName, SourceFile->LineNum, 0, "expected whitespace following #include keyword", NULL); 1278 } 1279 // 1280 // Should be quoted file name 1281 // 1282 if (SourceFile->FileBufferPtr[0] != UNICODE_DOUBLE_QUOTE) { 1283 Error (SourceFile->FileName, SourceFile->LineNum, 0, "expected quoted include file name", NULL); 1284 goto FailDone; 1285 } 1286 1287 SourceFile->FileBufferPtr++; 1288 // 1289 // Copy the filename as ascii to our local string 1290 // 1291 To = IncludeFileName; 1292 Len = 0; 1293 while (!EndOfFile (SourceFile)) { 1294 if ((SourceFile->FileBufferPtr[0] == UNICODE_CR) || (SourceFile->FileBufferPtr[0] == UNICODE_LF)) { 1295 Error (SourceFile->FileName, SourceFile->LineNum, 0, "end-of-line found in quoted include file name", NULL); 1296 goto FailDone; 1297 } 1298 1299 if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) { 1300 SourceFile->FileBufferPtr++; 1301 break; 1302 } 1303 // 1304 // If too long, then report the error once and process until the closing quote 1305 // 1306 Len++; 1307 if (!ReportedError && (Len >= sizeof (IncludeFileName))) { 1308 Error (SourceFile->FileName, SourceFile->LineNum, 0, "length of include file name exceeds limit", NULL); 1309 ReportedError = TRUE; 1310 } 1311 1312 if (!ReportedError) { 1313 *To = UNICODE_TO_ASCII (SourceFile->FileBufferPtr[0]); 1314 To++; 1315 } 1316 1317 SourceFile->FileBufferPtr++; 1318 } 1319 1320 if (!ReportedError) { 1321 *To = 0; 1322 memset ((char *) &IncludedSourceFile, 0, sizeof (SOURCE_FILE)); 1323 strcpy (IncludedSourceFile.FileName, IncludeFileName); 1324 IncludedSourceFile.ControlCharacter = DEFAULT_CONTROL_CHARACTER; 1325 ProcessIncludeFile (&IncludedSourceFile, SourceFile); 1326 // 1327 // printf ("including file '%s'\n", IncludeFileName); 1328 // 1329 } 1330 1331 return ; 1332 FailDone: 1333 // 1334 // Error recovery -- skip to next # 1335 // 1336 SourceFile->SkipToHash = TRUE; 1337 } 1338 1339 static 1340 void 1341 ProcessTokenScope ( 1342 SOURCE_FILE *SourceFile 1343 ) 1344 { 1345 WCHAR StringIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; 1346 // 1347 // Extract the scope name 1348 // 1349 if (GetStringIdentifierName (SourceFile, StringIdentifier, sizeof (StringIdentifier)) > 0) { 1350 StringDBSetScope (StringIdentifier); 1351 } 1352 } 1353 1354 // 1355 // Parse: #langdef eng "English" 1356 // #langdef chn "\wideChinese" 1357 // 1358 static 1359 void 1360 ProcessTokenLangDef ( 1361 SOURCE_FILE *SourceFile 1362 ) 1363 { 1364 STATUS Status; 1365 WCHAR LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; 1366 WCHAR *PrintableName; 1367 1368 Status = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE); 1369 if (Status != STATUS_SUCCESS) { 1370 return; 1371 } 1372 1373 // 1374 // Extract the printable name 1375 // 1376 PrintableName = GetPrintableLanguageName (SourceFile); 1377 if (PrintableName != NULL) { 1378 ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); 1379 StringDBAddLanguage (LanguageIdentifier, PrintableName, NULL); 1380 FREE (PrintableName); 1381 return ; 1382 } 1383 // 1384 // Error recovery -- skip to next # 1385 // 1386 SourceFile->SkipToHash = TRUE; 1387 } 1388 1389 static 1390 VOID 1391 ProcessTokenSecondaryLangDef ( 1392 SOURCE_FILE *SourceFile 1393 ) 1394 { 1395 STATUS Status; 1396 LANGUAGE_LIST *Lang; 1397 WCHAR LanguageIdentifier[MAX_STRING_IDENTIFIER_NAME + 1]; 1398 WCHAR *LangCode; 1399 WCHAR *SecondaryLangList = NULL; 1400 1401 Status = GetLanguageIdentifierName (SourceFile, LanguageIdentifier, sizeof (LanguageIdentifier), FALSE); 1402 if (Status != STATUS_SUCCESS) { 1403 return; 1404 } 1405 LangCode = GetLangCode(LanguageIdentifier); 1406 if (LangCode == NULL) { 1407 return ; 1408 } 1409 1410 Lang = StringDBFindLanguageList (LanguageIdentifier); 1411 if (Lang == NULL) { 1412 return; 1413 } 1414 1415 SecondaryLangList = GetSecondaryLanguageList (SourceFile); 1416 if (SecondaryLangList != NULL) { 1417 ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); 1418 Status = StringDBAddSecondaryLanguage (LangCode, GetLangCodeList(SecondaryLangList)); 1419 if (Status != STATUS_SUCCESS) { 1420 SourceFile->SkipToHash = TRUE; 1421 } 1422 FREE (LangCode); 1423 FREE (SecondaryLangList); 1424 return ; 1425 } 1426 FREE (LangCode); 1427 1428 1429 SourceFile->SkipToHash = TRUE; 1430 } 1431 1432 static 1433 BOOLEAN 1434 ApparentQuotedString ( 1435 SOURCE_FILE *SourceFile 1436 ) 1437 { 1438 WCHAR *Ptr; 1439 // 1440 // See if the first and last nonblank characters on the line are double quotes 1441 // 1442 for (Ptr = SourceFile->FileBufferPtr; *Ptr && (*Ptr == UNICODE_SPACE); Ptr++) 1443 ; 1444 if (*Ptr != UNICODE_DOUBLE_QUOTE) { 1445 return FALSE; 1446 } 1447 1448 while (*Ptr) { 1449 Ptr++; 1450 } 1451 1452 Ptr--; 1453 for (; *Ptr && (*Ptr == UNICODE_SPACE); Ptr--) 1454 ; 1455 if (*Ptr != UNICODE_DOUBLE_QUOTE) { 1456 return FALSE; 1457 } 1458 1459 return TRUE; 1460 } 1461 // 1462 // Parse: 1463 // #language eng "some string " "more string" 1464 // 1465 static 1466 void 1467 ProcessTokenLanguage ( 1468 SOURCE_FILE *SourceFile 1469 ) 1470 { 1471 STATUS Status; 1472 WCHAR *String; 1473 WCHAR *SecondString; 1474 WCHAR *TempString; 1475 WCHAR *From; 1476 WCHAR *To; 1477 WCHAR Language[LANGUAGE_IDENTIFIER_NAME_LEN + 1]; 1478 UINT32 Len; 1479 BOOLEAN PreviousNewline; 1480 // 1481 // Get the language identifier 1482 // 1483 Language[0] = 0; 1484 Status = GetLanguageIdentifierName (SourceFile, Language, sizeof (Language), TRUE); 1485 if (Status != STATUS_SUCCESS) { 1486 return; 1487 } 1488 1489 // 1490 // Extract the string value. It's either a quoted string that starts on the current line, or 1491 // an unquoted string that starts on the following line and continues until the next control 1492 // character in column 1. 1493 // Look ahead to find a quote or a newline 1494 // 1495 if (SkipTo (SourceFile, UNICODE_DOUBLE_QUOTE, TRUE)) { 1496 String = GetQuotedString (SourceFile, FALSE); 1497 if (String != NULL) { 1498 // 1499 // Set the position in the file of where we are parsing for error 1500 // reporting purposes. Then start looking ahead for additional 1501 // quoted strings, and concatenate them until we get a failure 1502 // back from the string parser. 1503 // 1504 Len = wcslen (String) + 1; 1505 ParserSetPosition (SourceFile->FileName, SourceFile->LineNum); 1506 do { 1507 SkipWhiteSpace (SourceFile); 1508 SecondString = GetQuotedString (SourceFile, TRUE); 1509 if (SecondString != NULL) { 1510 Len += wcslen (SecondString); 1511 TempString = (WCHAR *) malloc (Len * sizeof (WCHAR)); 1512 if (TempString == NULL) { 1513 Error (NULL, 0, 0, "application error", "failed to allocate memory"); 1514 return ; 1515 } 1516 1517 wcscpy (TempString, String); 1518 wcscat (TempString, SecondString); 1519 free (String); 1520 free (SecondString); 1521 String = TempString; 1522 } 1523 } while (SecondString != NULL); 1524 StringDBAddString (Language, NULL, NULL, String, TRUE, 0); 1525 free (String); 1526 } else { 1527 // 1528 // Error was reported at lower level. Error recovery mode. 1529 // 1530 SourceFile->SkipToHash = TRUE; 1531 } 1532 } else { 1533 if (!mGlobals.UnquotedStrings) { 1534 // 1535 // They're using unquoted strings. If the next non-blank character is a double quote, and the 1536 // last non-blank character on the line is a double quote, then more than likely they're using 1537 // quotes, so they need to put the quoted string on the end of the previous line 1538 // 1539 if (ApparentQuotedString (SourceFile)) { 1540 Warning ( 1541 SourceFile->FileName, 1542 SourceFile->LineNum, 1543 0, 1544 "unexpected quoted string on line", 1545 "specify -uqs option if necessary" 1546 ); 1547 } 1548 } 1549 // 1550 // Found end-of-line (hopefully). Skip over it and start taking in characters 1551 // until we find a control character at the start of a line. 1552 // 1553 Len = 0; 1554 From = SourceFile->FileBufferPtr; 1555 PreviousNewline = FALSE; 1556 while (!EndOfFile (SourceFile)) { 1557 if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { 1558 PreviousNewline = TRUE; 1559 SourceFile->LineNum++; 1560 } else { 1561 Len++; 1562 if (PreviousNewline && (SourceFile->FileBufferPtr[0] == SourceFile->ControlCharacter)) { 1563 break; 1564 } 1565 1566 PreviousNewline = FALSE; 1567 } 1568 1569 SourceFile->FileBufferPtr++; 1570 } 1571 1572 if ((Len == 0) && EndOfFile (SourceFile)) { 1573 Error (SourceFile->FileName, SourceFile->LineNum, 0, "unexpected end of file", NULL); 1574 SourceFile->SkipToHash = TRUE; 1575 return ; 1576 } 1577 // 1578 // Now allocate a buffer, copy the characters, and add the string. 1579 // 1580 String = (WCHAR *) malloc ((Len + 1) * sizeof (WCHAR)); 1581 if (String == NULL) { 1582 Error (NULL, 0, 0, "application error", "failed to allocate memory"); 1583 return ; 1584 } 1585 1586 To = String; 1587 while (From < SourceFile->FileBufferPtr) { 1588 switch (*From) { 1589 case UNICODE_LF: 1590 case 0: 1591 break; 1592 1593 default: 1594 *To = *From; 1595 To++; 1596 break; 1597 } 1598 1599 From++; 1600 } 1601 1602 // 1603 // String[Len] = 0; 1604 // 1605 *To = 0; 1606 StringDBAddString (Language, NULL, NULL, String, TRUE, 0); 1607 } 1608 } 1609 1610 static 1611 BOOLEAN 1612 IsWhiteSpace ( 1613 SOURCE_FILE *SourceFile 1614 ) 1615 { 1616 switch (SourceFile->FileBufferPtr[0]) { 1617 case UNICODE_NULL: 1618 case UNICODE_CR: 1619 case UNICODE_SPACE: 1620 case UNICODE_TAB: 1621 case UNICODE_LF: 1622 return TRUE; 1623 1624 default: 1625 return FALSE; 1626 } 1627 } 1628 1629 static 1630 UINT32 1631 SkipWhiteSpace ( 1632 SOURCE_FILE *SourceFile 1633 ) 1634 { 1635 UINT32 Count; 1636 1637 Count = 0; 1638 while (!EndOfFile (SourceFile)) { 1639 Count++; 1640 switch (*SourceFile->FileBufferPtr) { 1641 case UNICODE_NULL: 1642 case UNICODE_CR: 1643 case UNICODE_SPACE: 1644 case UNICODE_TAB: 1645 SourceFile->FileBufferPtr++; 1646 break; 1647 1648 case UNICODE_LF: 1649 SourceFile->FileBufferPtr++; 1650 SourceFile->LineNum++; 1651 if (mGlobals.Verbose) { 1652 printf ("%d: %S\n", SourceFile->LineNum, SourceFile->FileBufferPtr); 1653 } 1654 break; 1655 1656 default: 1657 return Count - 1; 1658 } 1659 } 1660 // 1661 // Some tokens require trailing whitespace. If we're at the end of the 1662 // file, then we count that as well. 1663 // 1664 if ((Count == 0) && (EndOfFile (SourceFile))) { 1665 Count++; 1666 } 1667 1668 return Count; 1669 } 1670 1671 static 1672 UINT32 1673 wstrcmp ( 1674 WCHAR *Buffer, 1675 WCHAR *Str 1676 ) 1677 { 1678 UINT32 Len; 1679 1680 Len = 0; 1681 while (*Str == *Buffer) { 1682 Buffer++; 1683 Str++; 1684 Len++; 1685 } 1686 1687 if (*Str) { 1688 return 0; 1689 } 1690 1691 return Len; 1692 } 1693 1694 static 1695 WCHAR * 1696 wstrcatenate ( 1697 WCHAR *Dst, 1698 WCHAR *Src 1699 ) 1700 { 1701 UINT32 Len = 0; 1702 WCHAR *Bak = Dst; 1703 1704 if (Src == NULL) { 1705 return Dst; 1706 } 1707 1708 if (Dst != NULL) { 1709 Len = wcslen (Dst); 1710 } 1711 Len += wcslen (Src); 1712 Dst = (WCHAR *) malloc ((Len + 1) * 2); 1713 if (Dst == NULL) { 1714 return NULL; 1715 } 1716 1717 Dst[0] = L'\0'; 1718 if (Bak != NULL) { 1719 wcscpy (Dst, Bak); 1720 FREE (Bak); 1721 } 1722 wcscat (Dst, Src); 1723 return Dst; 1724 } 1725 1726 // 1727 // Given a filename, try to find it along the include paths. 1728 // 1729 static 1730 FILE * 1731 FindFile ( 1732 IN INT8 *FileName, 1733 OUT INT8 *FoundFileName, 1734 IN UINT32 FoundFileNameLen 1735 ) 1736 { 1737 FILE *Fptr; 1738 TEXT_STRING_LIST *List; 1739 1740 // 1741 // Traverse the list of paths and try to find the file 1742 // 1743 List = mGlobals.IncludePaths; 1744 while (List != NULL) { 1745 // 1746 // Put the path and filename together 1747 // 1748 if (strlen (List->Str) + strlen (FileName) + 1 > FoundFileNameLen) { 1749 Error (UTILITY_NAME, 0, 0, NULL, "internal error - cannot concatenate path+filename"); 1750 return NULL; 1751 } 1752 // 1753 // Append the filename to this include path and try to open the file. 1754 // 1755 strcpy (FoundFileName, List->Str); 1756 strcat (FoundFileName, FileName); 1757 if ((Fptr = fopen (FoundFileName, "rb")) != NULL) { 1758 // 1759 // Return the file pointer 1760 // 1761 return Fptr; 1762 } 1763 1764 List = List->Next; 1765 } 1766 // 1767 // Not found 1768 // 1769 FoundFileName[0] = 0; 1770 return NULL; 1771 } 1772 // 1773 // Process the command-line arguments 1774 // 1775 static 1776 STATUS 1777 ProcessArgs ( 1778 int Argc, 1779 char *Argv[] 1780 ) 1781 { 1782 TEXT_STRING_LIST *NewList; 1783 char *Cptr; 1784 char *Cptr2; 1785 1786 // 1787 // Clear our globals 1788 // 1789 memset ((char *) &mGlobals, 0, sizeof (mGlobals)); 1790 strcpy (mGlobals.BaseName, DEFAULT_BASE_NAME); 1791 // 1792 // Skip program name 1793 // 1794 Argc--; 1795 Argv++; 1796 1797 if (Argc == 0) { 1798 Usage (); 1799 return STATUS_ERROR; 1800 } 1801 1802 mGlobals.Mode = MODE_UNKNOWN; 1803 // 1804 // Process until no more -args. 1805 // 1806 while ((Argc > 0) && (Argv[0][0] == '-')) { 1807 // 1808 // -parse option 1809 // 1810 if (_stricmp (Argv[0], "-parse") == 0) { 1811 if (mGlobals.Mode != MODE_UNKNOWN) { 1812 Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); 1813 return STATUS_ERROR; 1814 } 1815 1816 mGlobals.Mode = MODE_PARSE; 1817 // 1818 // -scan option 1819 // 1820 } else if (_stricmp (Argv[0], "-scan") == 0) { 1821 if (mGlobals.Mode != MODE_UNKNOWN) { 1822 Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); 1823 return STATUS_ERROR; 1824 } 1825 1826 mGlobals.Mode = MODE_SCAN; 1827 // 1828 // -vscan verbose scanning option 1829 // 1830 } else if (_stricmp (Argv[0], "-vscan") == 0) { 1831 mGlobals.VerboseScan = TRUE; 1832 // 1833 // -dump option 1834 // 1835 } else if (_stricmp (Argv[0], "-dump") == 0) { 1836 if (mGlobals.Mode != MODE_UNKNOWN) { 1837 Error (NULL, 0, 0, "only one of -parse/-scan/-dump allowed", NULL); 1838 return STATUS_ERROR; 1839 } 1840 1841 mGlobals.Mode = MODE_DUMP; 1842 } else if (_stricmp (Argv[0], "-uqs") == 0) { 1843 mGlobals.UnquotedStrings = TRUE; 1844 // 1845 // -i path add include search path when parsing 1846 // 1847 } else if (_stricmp (Argv[0], "-i") == 0) { 1848 // 1849 // check for one more arg 1850 // 1851 if ((Argc <= 1) || (Argv[1][0] == '-')) { 1852 Error (UTILITY_NAME, 0, 0, Argv[0], "missing include path"); 1853 return STATUS_ERROR; 1854 } 1855 // 1856 // Allocate memory for a new list element, fill it in, and 1857 // add it to our list of include paths. Always make sure it 1858 // has a "\" on the end of it. 1859 // 1860 NewList = malloc (sizeof (TEXT_STRING_LIST)); 1861 if (NewList == NULL) { 1862 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 1863 return STATUS_ERROR; 1864 } 1865 1866 memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); 1867 NewList->Str = malloc (strlen (Argv[1]) + 2); 1868 if (NewList->Str == NULL) { 1869 free (NewList); 1870 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 1871 return STATUS_ERROR; 1872 } 1873 1874 strcpy (NewList->Str, Argv[1]); 1875 if (NewList->Str[strlen (NewList->Str) - 1] != '\\') { 1876 strcat (NewList->Str, "\\"); 1877 } 1878 // 1879 // Add it to our linked list 1880 // 1881 if (mGlobals.IncludePaths == NULL) { 1882 mGlobals.IncludePaths = NewList; 1883 } else { 1884 mGlobals.LastIncludePath->Next = NewList; 1885 } 1886 1887 mGlobals.LastIncludePath = NewList; 1888 Argc--; 1889 Argv++; 1890 } else if (_stricmp (Argv[0], "-if") == 0) { 1891 // 1892 // Indirection file -- check for one more arg 1893 // 1894 if ((Argc <= 1) || (Argv[1][0] == '-')) { 1895 Error (UTILITY_NAME, 0, 0, Argv[0], "missing indirection file name"); 1896 return STATUS_ERROR; 1897 } 1898 // 1899 // Allocate memory for a new list element, fill it in, and 1900 // add it to our list of include paths. Always make sure it 1901 // has a "\" on the end of it. 1902 // 1903 NewList = malloc (sizeof (TEXT_STRING_LIST)); 1904 if (NewList == NULL) { 1905 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 1906 return STATUS_ERROR; 1907 } 1908 1909 memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); 1910 NewList->Str = malloc (strlen (Argv[1]) + 1); 1911 if (NewList->Str == NULL) { 1912 free (NewList); 1913 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 1914 return STATUS_ERROR; 1915 } 1916 1917 strcpy (NewList->Str, Argv[1]); 1918 // 1919 // Add it to our linked list 1920 // 1921 if (mGlobals.IndirectionFileName == NULL) { 1922 mGlobals.IndirectionFileName = NewList; 1923 } else { 1924 mGlobals.LastIndirectionFileName->Next = NewList; 1925 } 1926 1927 mGlobals.LastIndirectionFileName = NewList; 1928 Argc--; 1929 Argv++; 1930 } else if (_stricmp (Argv[0], "-db") == 0) { 1931 // 1932 // -db option to specify a database file. 1933 // Check for one more arg (the database file name) 1934 // 1935 if ((Argc <= 1) || (Argv[1][0] == '-')) { 1936 Error (UTILITY_NAME, 0, 0, Argv[0], "missing database file name"); 1937 return STATUS_ERROR; 1938 } 1939 1940 NewList = malloc (sizeof (TEXT_STRING_LIST)); 1941 if (NewList == NULL) { 1942 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 1943 return STATUS_ERROR; 1944 } 1945 1946 memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); 1947 NewList->Str = malloc (strlen (Argv[1]) + 1); 1948 if (NewList->Str == NULL) { 1949 free (NewList); 1950 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 1951 return STATUS_ERROR; 1952 } 1953 1954 strcpy (NewList->Str, Argv[1]); 1955 // 1956 // Add it to our linked list 1957 // 1958 if (mGlobals.DatabaseFileName == NULL) { 1959 mGlobals.DatabaseFileName = NewList; 1960 } else { 1961 mGlobals.LastDatabaseFileName->Next = NewList; 1962 } 1963 1964 mGlobals.LastDatabaseFileName = NewList; 1965 Argc--; 1966 Argv++; 1967 } else if (_stricmp (Argv[0], "-ou") == 0) { 1968 // 1969 // -ou option to specify an output unicode file to 1970 // which we can dump our database. 1971 // 1972 if ((Argc <= 1) || (Argv[1][0] == '-')) { 1973 Error (UTILITY_NAME, 0, 0, Argv[0], "missing database dump output file name"); 1974 return STATUS_ERROR; 1975 } 1976 1977 if (mGlobals.DumpUFileName[0] == 0) { 1978 strcpy (mGlobals.DumpUFileName, Argv[1]); 1979 } else { 1980 Error (UTILITY_NAME, 0, 0, Argv[1], "-ou option already specified with '%s'", mGlobals.DumpUFileName); 1981 return STATUS_ERROR; 1982 } 1983 1984 Argc--; 1985 Argv++; 1986 } else if (_stricmp (Argv[0], "-hpk") == 0) { 1987 // 1988 // -hpk option to create an HII export pack of the input database file 1989 // 1990 if ((Argc <= 1) || (Argv[1][0] == '-')) { 1991 Error (UTILITY_NAME, 0, 0, Argv[0], "missing raw string data dump output file name"); 1992 return STATUS_ERROR; 1993 } 1994 1995 if (mGlobals.HiiExportPackFileName[0] == 0) { 1996 strcpy (mGlobals.HiiExportPackFileName, Argv[1]); 1997 } else { 1998 Error (UTILITY_NAME, 0, 0, Argv[1], "-or option already specified with '%s'", mGlobals.HiiExportPackFileName); 1999 return STATUS_ERROR; 2000 } 2001 2002 Argc--; 2003 Argv++; 2004 } else if ((_stricmp (Argv[0], "-?") == 0) || (_stricmp (Argv[0], "-h") == 0)) { 2005 Usage (); 2006 return STATUS_ERROR; 2007 } else if (_stricmp (Argv[0], "-v") == 0) { 2008 mGlobals.Verbose = 1; 2009 } else if (_stricmp (Argv[0], "-vdbw") == 0) { 2010 mGlobals.VerboseDatabaseWrite = 1; 2011 } else if (_stricmp (Argv[0], "-vdbr") == 0) { 2012 mGlobals.VerboseDatabaseRead = 1; 2013 } else if (_stricmp (Argv[0], "-newdb") == 0) { 2014 mGlobals.NewDatabase = 1; 2015 } else if (_stricmp (Argv[0], "-ignorenotfound") == 0) { 2016 mGlobals.IgnoreNotFound = 1; 2017 } else if (_stricmp (Argv[0], "-oc") == 0) { 2018 // 2019 // check for one more arg 2020 // 2021 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2022 Error (UTILITY_NAME, 0, 0, Argv[0], "missing output C filename"); 2023 return STATUS_ERROR; 2024 } 2025 2026 strcpy (mGlobals.StringCFileName, Argv[1]); 2027 Argc--; 2028 Argv++; 2029 } else if (_stricmp (Argv[0], "-bn") == 0) { 2030 // 2031 // check for one more arg 2032 // 2033 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2034 Error (UTILITY_NAME, 0, 0, Argv[0], "missing base name"); 2035 Usage (); 2036 return STATUS_ERROR; 2037 } 2038 2039 strcpy (mGlobals.BaseName, Argv[1]); 2040 Argc--; 2041 Argv++; 2042 } else if (_stricmp (Argv[0], "-oh") == 0) { 2043 // 2044 // -oh to specify output .h defines file name 2045 // 2046 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2047 Error (UTILITY_NAME, 0, 0, Argv[0], "missing output .h filename"); 2048 return STATUS_ERROR; 2049 } 2050 2051 strcpy (mGlobals.StringHFileName, Argv[1]); 2052 Argc--; 2053 Argv++; 2054 } else if (_stricmp (Argv[0], "-dep") == 0) { 2055 // 2056 // -dep to specify output dependency file name 2057 // 2058 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2059 Error (UTILITY_NAME, 0, 0, Argv[0], "missing output dependency filename"); 2060 return STATUS_ERROR; 2061 } 2062 2063 strcpy (mGlobals.OutputDependencyFileName, Argv[1]); 2064 Argc--; 2065 Argv++; 2066 } else if (_stricmp (Argv[0], "-skipext") == 0) { 2067 // 2068 // -skipext to skip scanning of files with certain filename extensions 2069 // 2070 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2071 Error (UTILITY_NAME, 0, 0, Argv[0], "missing filename extension"); 2072 return STATUS_ERROR; 2073 } 2074 // 2075 // Allocate memory for a new list element, fill it in, and 2076 // add it to our list of excluded extensions. Always make sure it 2077 // has a "." as the first character. 2078 // 2079 NewList = malloc (sizeof (TEXT_STRING_LIST)); 2080 if (NewList == NULL) { 2081 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 2082 return STATUS_ERROR; 2083 } 2084 2085 memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); 2086 NewList->Str = malloc (strlen (Argv[1]) + 2); 2087 if (NewList->Str == NULL) { 2088 free (NewList); 2089 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 2090 return STATUS_ERROR; 2091 } 2092 2093 if (Argv[1][0] == '.') { 2094 strcpy (NewList->Str, Argv[1]); 2095 } else { 2096 NewList->Str[0] = '.'; 2097 strcpy (NewList->Str + 1, Argv[1]); 2098 } 2099 // 2100 // Add it to our linked list 2101 // 2102 if (mGlobals.SkipExt == NULL) { 2103 mGlobals.SkipExt = NewList; 2104 } else { 2105 mGlobals.LastSkipExt->Next = NewList; 2106 } 2107 2108 mGlobals.LastSkipExt = NewList; 2109 Argc--; 2110 Argv++; 2111 } else if (_stricmp (Argv[0], "-lang") == 0) { 2112 // 2113 // "-lang zh-Hans" or "-lang en-US" to only output certain languages 2114 // 2115 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2116 Error (UTILITY_NAME, 0, 0, Argv[0], "missing language name"); 2117 Usage (); 2118 return STATUS_ERROR; 2119 } 2120 2121 if (AddCommandLineLanguage (Argv[1]) != STATUS_SUCCESS) { 2122 return STATUS_ERROR; 2123 } 2124 2125 Argc--; 2126 Argv++; 2127 } else if (_stricmp (Argv[0], "-od") == 0) { 2128 // 2129 // Output database file name -- check for another arg 2130 // 2131 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2132 Error (UTILITY_NAME, 0, 0, Argv[0], "missing output database file name"); 2133 return STATUS_ERROR; 2134 } 2135 2136 strcpy (mGlobals.OutputDatabaseFileName, Argv[1]); 2137 Argv++; 2138 Argc--; 2139 } else if (_stricmp (Argv[0], "-ppflag") == 0) { 2140 // 2141 // -ppflag "Preprocess flags" -- check for another arg 2142 // 2143 if ((Argc <= 1) || (Argv[1][0] == '-')) { 2144 Error (UTILITY_NAME, 0, 0, Argv[0], "missing preprocess flags"); 2145 Usage (); 2146 return STATUS_ERROR; 2147 } 2148 2149 // 2150 // Allocate memory for a new list element, fill it in, and 2151 // add it to our list of preprocess flag. 2152 // 2153 NewList = malloc (sizeof (TEXT_STRING_LIST)); 2154 if (NewList == NULL) { 2155 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 2156 return STATUS_ERROR; 2157 } 2158 2159 memset ((char *) NewList, 0, sizeof (TEXT_STRING_LIST)); 2160 NewList->Str = malloc (strlen (Argv[1]) * 2 + 1); 2161 if (NewList->Str == NULL) { 2162 free (NewList); 2163 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 2164 return STATUS_ERROR; 2165 } 2166 2167 // 2168 // Convert '"' to '\"' in preprocess flag 2169 // 2170 Cptr = Argv[1]; 2171 Cptr2 = NewList->Str; 2172 if (*Cptr == '"') { 2173 *Cptr2++ = '\\'; 2174 *Cptr2++ = '"'; 2175 Cptr++; 2176 } 2177 while (*Cptr != '\0') { 2178 if ((*Cptr == '"') && (*(Cptr - 1) != '\\')) { 2179 *Cptr2++ = '\\'; 2180 } 2181 *Cptr2++ = *Cptr++; 2182 } 2183 *Cptr2 = '\0'; 2184 2185 // 2186 // Add it to our linked list 2187 // 2188 if (mGlobals.PreprocessFlags == NULL) { 2189 mGlobals.PreprocessFlags = NewList; 2190 } else { 2191 mGlobals.LastPreprocessFlags->Next = NewList; 2192 } 2193 mGlobals.LastPreprocessFlags = NewList; 2194 2195 mGlobals.Preprocess = TRUE; 2196 2197 Argv++; 2198 Argc--; 2199 } else { 2200 // 2201 // Unrecognized arg 2202 // 2203 Error (UTILITY_NAME, 0, 0, Argv[0], "unrecognized option"); 2204 Usage (); 2205 return STATUS_ERROR; 2206 } 2207 2208 Argv++; 2209 Argc--; 2210 } 2211 // 2212 // Make sure they specified the mode parse/scan/dump 2213 // 2214 if (mGlobals.Mode == MODE_UNKNOWN) { 2215 Error (NULL, 0, 0, "must specify one of -parse/-scan/-dump", NULL); 2216 return STATUS_ERROR; 2217 } 2218 // 2219 // All modes require a database filename 2220 // 2221 if (mGlobals.DatabaseFileName == 0) { 2222 Error (NULL, 0, 0, "must specify a database filename using -db DbFileName", NULL); 2223 Usage (); 2224 return STATUS_ERROR; 2225 } 2226 // 2227 // If dumping the database file, then return immediately if all 2228 // parameters check out. 2229 // 2230 if (mGlobals.Mode == MODE_DUMP) { 2231 // 2232 // Not much use if they didn't specify -oh or -oc or -ou or -hpk 2233 // 2234 if ((mGlobals.DumpUFileName[0] == 0) && 2235 (mGlobals.StringHFileName[0] == 0) && 2236 (mGlobals.StringCFileName[0] == 0) && 2237 (mGlobals.HiiExportPackFileName[0] == 0) 2238 ) { 2239 Error (NULL, 0, 0, "-dump without -oc/-oh/-ou/-hpk is a NOP", NULL); 2240 return STATUS_ERROR; 2241 } 2242 2243 return STATUS_SUCCESS; 2244 } 2245 // 2246 // Had to specify source string file and output string defines header filename. 2247 // 2248 if (mGlobals.Mode == MODE_SCAN) { 2249 if (Argc < 1) { 2250 Error (UTILITY_NAME, 0, 0, NULL, "must specify at least one source file to scan with -scan"); 2251 Usage (); 2252 return STATUS_ERROR; 2253 } 2254 // 2255 // If -ppflag is specified, -oh should also be specified for preprocess 2256 // 2257 if (mGlobals.Preprocess && (mGlobals.StringHFileName[0] == 0)) { 2258 Error (UTILITY_NAME, 0, 0, NULL, "must specify string defines file name to preprocess before scan"); 2259 Usage (); 2260 return STATUS_ERROR; 2261 } 2262 // 2263 // Get the list of filenames 2264 // 2265 while (Argc > 0) { 2266 NewList = malloc (sizeof (TEXT_STRING_LIST)); 2267 if (NewList == NULL) { 2268 Error (UTILITY_NAME, 0, 0, "memory allocation failure", NULL); 2269 return STATUS_ERROR; 2270 } 2271 2272 memset (NewList, 0, sizeof (TEXT_STRING_LIST)); 2273 NewList->Str = (UINT8 *) malloc (strlen (Argv[0]) + 1); 2274 if (NewList->Str == NULL) { 2275 Error (UTILITY_NAME, 0, 0, "memory allocation failure", NULL); 2276 return STATUS_ERROR; 2277 } 2278 2279 strcpy (NewList->Str, Argv[0]); 2280 if (mGlobals.ScanFileName == NULL) { 2281 mGlobals.ScanFileName = NewList; 2282 } else { 2283 mGlobals.LastScanFileName->Next = NewList; 2284 } 2285 2286 mGlobals.LastScanFileName = NewList; 2287 Argc--; 2288 Argv++; 2289 } 2290 } else { 2291 // 2292 // Parse mode -- must specify an input unicode file name 2293 // 2294 if (Argc < 1) { 2295 Error (UTILITY_NAME, 0, 0, NULL, "must specify input unicode string file name with -parse"); 2296 Usage (); 2297 return STATUS_ERROR; 2298 } 2299 2300 strcpy (mGlobals.SourceFiles.FileName, Argv[0]); 2301 } 2302 2303 return STATUS_SUCCESS; 2304 } 2305 // 2306 // Found "-lang zh-Hans;en-US" on the command line. Parse the 2307 // language list and save the setting for later processing. 2308 // 2309 static 2310 STATUS 2311 AddCommandLineLanguage ( 2312 IN INT8 *Language 2313 ) 2314 { 2315 char Separator[] = ";"; 2316 char *Token; 2317 WCHAR_STRING_LIST *WNewList; 2318 2319 // 2320 // Keep processing the input string until we find the end. 2321 // 2322 Token = strtok (Language, Separator); 2323 while (Token != NULL) { 2324 WNewList = MALLOC (sizeof (WCHAR_STRING_LIST)); 2325 if (WNewList == NULL) { 2326 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 2327 return STATUS_ERROR; 2328 } 2329 WNewList->Next = NULL; 2330 WNewList->Str = MALLOC ((strlen (Token) + 1) * sizeof (WCHAR)); 2331 if (WNewList->Str == NULL) { 2332 free (WNewList); 2333 Error (UTILITY_NAME, 0, 0, NULL, "memory allocation failure"); 2334 return STATUS_ERROR; 2335 } 2336 #ifdef USE_VC8 2337 swprintf (WNewList->Str, (strlen (Token) + 1) * sizeof (WCHAR), L"%S", Token); 2338 #else 2339 swprintf (WNewList->Str, L"%S", Token); 2340 #endif 2341 2342 // 2343 // Add it to our linked list 2344 // 2345 if (mGlobals.Language == NULL) { 2346 mGlobals.Language = WNewList; 2347 } else { 2348 mGlobals.LastLanguage->Next = WNewList; 2349 } 2350 2351 mGlobals.LastLanguage = WNewList; 2352 Token = strtok (NULL, Separator); 2353 } 2354 2355 return STATUS_SUCCESS; 2356 } 2357 // 2358 // The contents of the text file are expected to be (one per line) 2359 // STRING_IDENTIFIER_NAME ScopeName 2360 // For example: 2361 // STR_ID_MY_FAVORITE_STRING IBM 2362 // 2363 static 2364 STATUS 2365 ParseIndirectionFiles ( 2366 TEXT_STRING_LIST *Files 2367 ) 2368 { 2369 FILE *Fptr; 2370 INT8 Line[200]; 2371 INT8 *StringName; 2372 INT8 *ScopeName; 2373 INT8 *End; 2374 UINT32 LineCount; 2375 WCHAR_MATCHING_STRING_LIST *NewList; 2376 2377 Line[sizeof (Line) - 1] = 0; 2378 Fptr = NULL; 2379 while (Files != NULL) { 2380 Fptr = fopen (Files->Str, "r"); 2381 LineCount = 0; 2382 if (Fptr == NULL) { 2383 Error (NULL, 0, 0, Files->Str, "failed to open input indirection file for reading"); 2384 return STATUS_ERROR; 2385 } 2386 2387 while (fgets (Line, sizeof (Line), Fptr) != NULL) { 2388 // 2389 // remove terminating newline for error printing purposes. 2390 // 2391 if (Line[strlen (Line) - 1] == '\n') { 2392 Line[strlen (Line) - 1] = 0; 2393 } 2394 2395 LineCount++; 2396 if (Line[sizeof (Line) - 1] != 0) { 2397 Error (Files->Str, LineCount, 0, "line length exceeds maximum supported", NULL); 2398 goto Done; 2399 } 2400 2401 StringName = Line; 2402 while (*StringName && (isspace (*StringName))) { 2403 StringName++; 2404 } 2405 2406 if (*StringName) { 2407 if ((*StringName == '_') || isalpha (*StringName)) { 2408 End = StringName; 2409 while ((*End) && (*End == '_') || (isalnum (*End))) { 2410 End++; 2411 } 2412 2413 if (isspace (*End)) { 2414 *End = 0; 2415 End++; 2416 while (isspace (*End)) { 2417 End++; 2418 } 2419 2420 if (*End) { 2421 ScopeName = End; 2422 while (*End && !isspace (*End)) { 2423 End++; 2424 } 2425 2426 *End = 0; 2427 // 2428 // Add the string name/scope pair 2429 // 2430 NewList = malloc (sizeof (WCHAR_MATCHING_STRING_LIST)); 2431 if (NewList == NULL) { 2432 Error (NULL, 0, 0, "memory allocation error", NULL); 2433 goto Done; 2434 } 2435 2436 memset (NewList, 0, sizeof (WCHAR_MATCHING_STRING_LIST)); 2437 NewList->Str1 = (WCHAR *) malloc ((strlen (StringName) + 1) * sizeof (WCHAR)); 2438 NewList->Str2 = (WCHAR *) malloc ((strlen (ScopeName) + 1) * sizeof (WCHAR)); 2439 if ((NewList->Str1 == NULL) || (NewList->Str2 == NULL)) { 2440 Error (NULL, 0, 0, "memory allocation error", NULL); 2441 goto Done; 2442 } 2443 2444 #ifdef USE_VC8 2445 swprintf (NewList->Str1, (strlen (StringName) + 1) * sizeof (WCHAR), L"%S", StringName); 2446 swprintf (NewList->Str2, (strlen (ScopeName) + 1) * sizeof (WCHAR), L"%S", ScopeName); 2447 #else 2448 swprintf (NewList->Str1, L"%S", StringName); 2449 swprintf (NewList->Str2, L"%S", ScopeName); 2450 #endif 2451 if (mGlobals.IndirectionList == NULL) { 2452 mGlobals.IndirectionList = NewList; 2453 } else { 2454 mGlobals.LastIndirectionList->Next = NewList; 2455 } 2456 2457 mGlobals.LastIndirectionList = NewList; 2458 } else { 2459 Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'"); 2460 goto Done; 2461 } 2462 } else { 2463 Error (Files->Str, LineCount, 0, StringName, "invalid line : expected 'StringIdentifier Scope'"); 2464 goto Done; 2465 } 2466 } else { 2467 Error (Files->Str, LineCount, 0, StringName, "invalid string identifier"); 2468 goto Done; 2469 } 2470 } 2471 } 2472 2473 fclose (Fptr); 2474 Fptr = NULL; 2475 Files = Files->Next; 2476 } 2477 2478 Done: 2479 if (Fptr != NULL) { 2480 fclose (Fptr); 2481 return STATUS_ERROR; 2482 } 2483 2484 return STATUS_SUCCESS; 2485 } 2486 2487 static 2488 INTN 2489 PreprocessSourceFile ( 2490 UINT8 *SourceFileName 2491 ) 2492 { 2493 char *Cptr; 2494 FILE *InFptr; 2495 FILE *OutFptr; 2496 UINT32 CmdLen; 2497 char *PreProcessCmd; 2498 char BaseName[MAX_PATH]; 2499 char TempFileName[MAX_PATH]; 2500 char SourceFileDir[MAX_PATH]; 2501 char Line[MAX_LINE_LEN]; 2502 TEXT_STRING_LIST *List; 2503 char InsertLine[] = "#undef STRING_TOKEN\n"; 2504 int Status; 2505 2506 // 2507 // Check whehter source file exist 2508 // 2509 InFptr = fopen (SourceFileName, "r"); 2510 if (InFptr == NULL) { 2511 Error (NULL, 0, 0, SourceFileName, "failed to open input file for scanning"); 2512 return STATUS_ERROR; 2513 } 2514 2515 // 2516 // Get source file directory 2517 // 2518 strcpy (SourceFileDir, SourceFileName); 2519 Cptr = strrchr (SourceFileDir, '\\'); 2520 if (Cptr != NULL) { 2521 *Cptr = '\0'; 2522 } 2523 2524 // 2525 // Generate preprocess output file name 2526 // 2527 strcpy (BaseName, mGlobals.OutputDatabaseFileName); 2528 Cptr = strrchr (BaseName, '\\'); 2529 if (Cptr != NULL) { 2530 *++Cptr = '\0'; 2531 } 2532 2533 Cptr = strrchr (SourceFileName, '\\'); 2534 if (Cptr != NULL) { 2535 Cptr++; 2536 } 2537 strcat (BaseName, Cptr); 2538 2539 Cptr = strrchr (BaseName, '.'); 2540 if (Cptr != NULL) { 2541 *Cptr = '\0'; 2542 } 2543 2544 strcpy (mGlobals.PreprocessFileName, BaseName); 2545 strcat (mGlobals.PreprocessFileName, PREPROCESS_OUTPUT_FILE_EXTENSION); 2546 2547 strcpy (TempFileName, BaseName); 2548 strcat (TempFileName, PREPROCESS_TEMP_FILE_EXTENSION); 2549 2550 // 2551 // Insert "#undef STRING_TOKEN" after each line of "#include ...", so as to 2552 // preserve the STRING_TOKEN() for scanning after preprocess 2553 // 2554 OutFptr = fopen (TempFileName, "w"); 2555 if (OutFptr == NULL) { 2556 Error (NULL, 0, 0, TempFileName, "failed to open file for write"); 2557 fclose (InFptr); 2558 return STATUS_ERROR; 2559 } 2560 while (fgets (Line, MAX_LINE_LEN, InFptr) != NULL) { 2561 fputs (Line, OutFptr); 2562 Cptr = Line; 2563 2564 // 2565 // Skip leading blank space 2566 // 2567 while (*Cptr == ' ' || *Cptr == '\t') { 2568 Cptr++; 2569 } 2570 2571 if (*Cptr == '#' && strncmp (Cptr + 1, "include", 7) == 0){ 2572 fputs (InsertLine, OutFptr); 2573 } 2574 } 2575 fclose (InFptr); 2576 fclose (OutFptr); 2577 2578 // 2579 // Prepare preprocess command 2580 // 2581 CmdLen = 1; 2582 CmdLen += strlen (PREPROCESSOR_COMMAND); 2583 CmdLen++; 2584 CmdLen += strlen (PREPROCESSOR_OPTIONS); 2585 CmdLen++; 2586 2587 // 2588 // "-I SourceFileDir " 2589 // 2590 CmdLen += strlen (SourceFileDir); 2591 CmdLen += 4; 2592 2593 List = mGlobals.PreprocessFlags; 2594 while (List != NULL) { 2595 CmdLen += strlen (List->Str); 2596 CmdLen++; 2597 2598 List = List->Next; 2599 } 2600 2601 CmdLen += strlen (TempFileName); 2602 CmdLen += 3; 2603 CmdLen += strlen (mGlobals.PreprocessFileName); 2604 2605 PreProcessCmd = malloc (CmdLen); 2606 if (PreProcessCmd == NULL) { 2607 Error (NULL, 0, 0, UTILITY_NAME, "memory allocation fail (%d bytes)\n", CmdLen); 2608 return STATUS_ERROR; 2609 } 2610 2611 strcpy (PreProcessCmd, PREPROCESSOR_COMMAND); 2612 strcat (PreProcessCmd, " "); 2613 strcat (PreProcessCmd, PREPROCESSOR_OPTIONS); 2614 strcat (PreProcessCmd, " "); 2615 2616 2617 strcat (PreProcessCmd, "-I "); 2618 strcat (PreProcessCmd, SourceFileDir); 2619 strcat (PreProcessCmd, " "); 2620 2621 List = mGlobals.PreprocessFlags; 2622 while (List != NULL) { 2623 strcat (PreProcessCmd, List->Str); 2624 strcat (PreProcessCmd, " "); 2625 2626 List = List->Next; 2627 } 2628 2629 strcat (PreProcessCmd, TempFileName); 2630 strcat (PreProcessCmd, " > "); 2631 strcat (PreProcessCmd, mGlobals.PreprocessFileName); 2632 2633 // 2634 // Preprocess the source file 2635 // 2636 Status = system (PreProcessCmd); 2637 if (Status != 0) { 2638 Error (NULL, 0, 0, PreProcessCmd, "failed to spawn C preprocessor on source file\n"); 2639 free (PreProcessCmd); 2640 return STATUS_ERROR; 2641 } 2642 2643 free (PreProcessCmd); 2644 return STATUS_SUCCESS; 2645 } 2646 2647 static 2648 STATUS 2649 ScanFiles ( 2650 TEXT_STRING_LIST *ScanFiles 2651 ) 2652 { 2653 char Line[MAX_LINE_LEN]; 2654 FILE *Fptr; 2655 char *FileName; 2656 UINT32 LineNum; 2657 char *Cptr; 2658 char *SavePtr; 2659 char *TermPtr; 2660 char *StringTokenPos; 2661 TEXT_STRING_LIST *SList; 2662 BOOLEAN SkipIt; 2663 BOOLEAN FileExist; 2664 2665 // 2666 // Put a null-terminator at the end of the line. If we read in 2667 // a line longer than we support, then we can catch it. 2668 // 2669 Line[MAX_LINE_LEN - 1] = 0; 2670 // 2671 // Process each file. If they gave us a skip extension list, then 2672 // skip it if the extension matches. 2673 // 2674 FileExist = FALSE; 2675 while (ScanFiles != NULL) { 2676 SkipIt = FALSE; 2677 for (SList = mGlobals.SkipExt; SList != NULL; SList = SList->Next) { 2678 if ((strlen (ScanFiles->Str) > strlen (SList->Str)) && 2679 (strcmp (ScanFiles->Str + strlen (ScanFiles->Str) - strlen (SList->Str), SList->Str) == 0) 2680 ) { 2681 SkipIt = TRUE; 2682 // 2683 // printf ("Match: %s : %s\n", ScanFiles->Str, SList->Str); 2684 // 2685 break; 2686 } 2687 } 2688 2689 if (!SkipIt) { 2690 if (mGlobals.VerboseScan) { 2691 printf ("Scanning %s\n", ScanFiles->Str); 2692 } 2693 2694 if (mGlobals.Preprocess) { 2695 // 2696 // Create an empty string defines file for preprocessor 2697 // 2698 if (!FileExist) { 2699 Fptr = fopen (mGlobals.StringHFileName, "w"); 2700 if (Fptr == NULL) { 2701 Error (NULL, 0, 0, mGlobals.StringHFileName, "failed to open file for write"); 2702 return STATUS_ERROR; 2703 } 2704 2705 fclose (Fptr); 2706 FileExist = TRUE; 2707 } 2708 2709 // 2710 // Preprocess using C preprocessor 2711 // 2712 if (PreprocessSourceFile (ScanFiles->Str) != STATUS_SUCCESS) { 2713 return STATUS_ERROR; 2714 } 2715 2716 FileName = mGlobals.PreprocessFileName; 2717 } else { 2718 FileName = ScanFiles->Str; 2719 } 2720 2721 Fptr = fopen (FileName, "r"); 2722 if (Fptr == NULL) { 2723 Error (NULL, 0, 0, FileName, "failed to open input file for scanning"); 2724 return STATUS_ERROR; 2725 } 2726 2727 LineNum = 0; 2728 while (fgets (Line, sizeof (Line), Fptr) != NULL) { 2729 LineNum++; 2730 if (Line[MAX_LINE_LEN - 1] != 0) { 2731 Error (ScanFiles->Str, LineNum, 0, "line length exceeds maximum supported by tool", NULL); 2732 fclose (Fptr); 2733 return STATUS_ERROR; 2734 } 2735 // 2736 // Remove the newline from the input line so we can print a warning message 2737 // 2738 if (Line[strlen (Line) - 1] == '\n') { 2739 Line[strlen (Line) - 1] = 0; 2740 } 2741 // 2742 // Terminate the line at // comments 2743 // 2744 Cptr = strstr (Line, "//"); 2745 if (Cptr != NULL) { 2746 *Cptr = 0; 2747 } 2748 2749 Cptr = Line; 2750 while ((Cptr = strstr (Cptr, STRING_TOKEN)) != NULL) { 2751 // 2752 // Found "STRING_TOKEN". Make sure we don't have NUM_STRING_TOKENS or 2753 // something like that. Then make sure it's followed by 2754 // an open parenthesis, a string identifier, and then a closing 2755 // parenthesis. 2756 // 2757 if (mGlobals.VerboseScan) { 2758 printf (" %d: %s", LineNum, Cptr); 2759 } 2760 2761 if (((Cptr == Line) || (!IsValidIdentifierChar (*(Cptr - 1), FALSE))) && 2762 (!IsValidIdentifierChar (*(Cptr + sizeof (STRING_TOKEN) - 1), FALSE)) 2763 ) { 2764 StringTokenPos = Cptr; 2765 SavePtr = Cptr; 2766 Cptr += strlen (STRING_TOKEN); 2767 while (*Cptr && isspace (*Cptr) && (*Cptr != '(')) { 2768 Cptr++; 2769 } 2770 2771 if (*Cptr != '(') { 2772 Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)"); 2773 } else { 2774 // 2775 // Skip over the open-parenthesis and find the next non-blank character 2776 // 2777 Cptr++; 2778 while (isspace (*Cptr)) { 2779 Cptr++; 2780 } 2781 2782 SavePtr = Cptr; 2783 if ((*Cptr == '_') || isalpha (*Cptr)) { 2784 while ((*Cptr == '_') || (isalnum (*Cptr))) { 2785 Cptr++; 2786 } 2787 2788 TermPtr = Cptr; 2789 while (*Cptr && isspace (*Cptr)) { 2790 Cptr++; 2791 } 2792 2793 if (*Cptr != ')') { 2794 Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected "STRING_TOKEN "(identifier)"); 2795 } 2796 2797 if (*TermPtr) { 2798 *TermPtr = 0; 2799 Cptr = TermPtr + 1; 2800 } else { 2801 Cptr = TermPtr; 2802 } 2803 // 2804 // Add the string identifier to the list of used strings 2805 // 2806 ParserSetPosition (ScanFiles->Str, LineNum); 2807 StringDBSetStringReferenced (SavePtr, mGlobals.IgnoreNotFound); 2808 if (mGlobals.VerboseScan) { 2809 printf ("...referenced %s", SavePtr); 2810 } 2811 } else { 2812 Warning (ScanFiles->Str, LineNum, 0, StringTokenPos, "expected valid string identifier name"); 2813 } 2814 } 2815 } else { 2816 // 2817 // Found it, but it's a substring of something else. Advance our pointer. 2818 // 2819 Cptr++; 2820 } 2821 2822 if (mGlobals.VerboseScan) { 2823 printf ("\n"); 2824 } 2825 } 2826 } 2827 2828 fclose (Fptr); 2829 } else { 2830 // 2831 // Skipping this file type 2832 // 2833 if (mGlobals.VerboseScan) { 2834 printf ("Skip scanning of %s\n", ScanFiles->Str); 2835 } 2836 } 2837 2838 ScanFiles = ScanFiles->Next; 2839 } 2840 2841 // 2842 // Remove the empty string defines file 2843 // 2844 if (FileExist) { 2845 remove (mGlobals.StringHFileName); 2846 } 2847 2848 return STATUS_SUCCESS; 2849 } 2850 // 2851 // Free the global string lists we allocated memory for 2852 // 2853 static 2854 void 2855 FreeLists ( 2856 VOID 2857 ) 2858 { 2859 TEXT_STRING_LIST *Temp; 2860 WCHAR_STRING_LIST *WTemp; 2861 2862 // 2863 // Traverse the include paths, freeing each 2864 // 2865 while (mGlobals.IncludePaths != NULL) { 2866 Temp = mGlobals.IncludePaths->Next; 2867 free (mGlobals.IncludePaths->Str); 2868 free (mGlobals.IncludePaths); 2869 mGlobals.IncludePaths = Temp; 2870 } 2871 // 2872 // If we did a scan, then free up our 2873 // list of files to scan. 2874 // 2875 while (mGlobals.ScanFileName != NULL) { 2876 Temp = mGlobals.ScanFileName->Next; 2877 free (mGlobals.ScanFileName->Str); 2878 free (mGlobals.ScanFileName); 2879 mGlobals.ScanFileName = Temp; 2880 } 2881 // 2882 // Free up preprocess flags list 2883 // 2884 while (mGlobals.PreprocessFlags != NULL) { 2885 Temp = mGlobals.PreprocessFlags->Next; 2886 free (mGlobals.PreprocessFlags->Str); 2887 free (mGlobals.PreprocessFlags); 2888 mGlobals.PreprocessFlags = Temp; 2889 } 2890 // 2891 // If they gave us a list of filename extensions to 2892 // skip on scan, then free them up. 2893 // 2894 while (mGlobals.SkipExt != NULL) { 2895 Temp = mGlobals.SkipExt->Next; 2896 free (mGlobals.SkipExt->Str); 2897 free (mGlobals.SkipExt); 2898 mGlobals.SkipExt = Temp; 2899 } 2900 // 2901 // Free up any languages specified 2902 // 2903 while (mGlobals.Language != NULL) { 2904 WTemp = mGlobals.Language->Next; 2905 free (mGlobals.Language->Str); 2906 free (mGlobals.Language); 2907 mGlobals.Language = WTemp; 2908 } 2909 // 2910 // Free up our indirection list 2911 // 2912 while (mGlobals.IndirectionList != NULL) { 2913 mGlobals.LastIndirectionList = mGlobals.IndirectionList->Next; 2914 free (mGlobals.IndirectionList->Str1); 2915 free (mGlobals.IndirectionList->Str2); 2916 free (mGlobals.IndirectionList); 2917 mGlobals.IndirectionList = mGlobals.LastIndirectionList; 2918 } 2919 2920 while (mGlobals.IndirectionFileName != NULL) { 2921 mGlobals.LastIndirectionFileName = mGlobals.IndirectionFileName->Next; 2922 free (mGlobals.IndirectionFileName->Str); 2923 free (mGlobals.IndirectionFileName); 2924 mGlobals.IndirectionFileName = mGlobals.LastIndirectionFileName; 2925 } 2926 } 2927 2928 static 2929 BOOLEAN 2930 IsValidIdentifierChar ( 2931 INT8 Char, 2932 BOOLEAN FirstChar 2933 ) 2934 { 2935 // 2936 // If it's the first character of an identifier, then 2937 // it must be one of [A-Za-z_]. 2938 // 2939 if (FirstChar) { 2940 if (isalpha (Char) || (Char == '_')) { 2941 return TRUE; 2942 } 2943 } else { 2944 // 2945 // If it's not the first character, then it can 2946 // be one of [A-Za-z_0-9] 2947 // 2948 if (isalnum (Char) || (Char == '_')) { 2949 return TRUE; 2950 } 2951 } 2952 2953 return FALSE; 2954 } 2955 2956 static 2957 void 2958 RewindFile ( 2959 SOURCE_FILE *SourceFile 2960 ) 2961 { 2962 SourceFile->LineNum = 1; 2963 SourceFile->FileBufferPtr = SourceFile->FileBuffer; 2964 SourceFile->EndOfFile = FALSE; 2965 } 2966 2967 static 2968 BOOLEAN 2969 SkipTo ( 2970 SOURCE_FILE *SourceFile, 2971 WCHAR WChar, 2972 BOOLEAN StopAfterNewline 2973 ) 2974 { 2975 while (!EndOfFile (SourceFile)) { 2976 // 2977 // Check for the character of interest 2978 // 2979 if (SourceFile->FileBufferPtr[0] == WChar) { 2980 return TRUE; 2981 } else { 2982 if (SourceFile->FileBufferPtr[0] == UNICODE_LF) { 2983 SourceFile->LineNum++; 2984 if (StopAfterNewline) { 2985 SourceFile->FileBufferPtr++; 2986 if (SourceFile->FileBufferPtr[0] == 0) { 2987 SourceFile->FileBufferPtr++; 2988 } 2989 2990 return FALSE; 2991 } 2992 } 2993 2994 SourceFile->FileBufferPtr++; 2995 } 2996 } 2997 2998 return FALSE; 2999 } 3000 3001 static 3002 void 3003 Usage ( 3004 VOID 3005 ) 3006 /*++ 3007 3008 Routine Description: 3009 3010 Print usage information for this utility. 3011 3012 Arguments: 3013 3014 None. 3015 3016 Returns: 3017 3018 Nothing. 3019 3020 --*/ 3021 { 3022 int Index; 3023 const char *Str[] = { 3024 UTILITY_NAME" "UTILITY_VERSION" - Intel UEFI String Gather Utility", 3025 " Copyright (C), 2004 - 2008 Intel Corporation", 3026 3027 #if ( defined(UTILITY_BUILD) && defined(UTILITY_VENDOR) ) 3028 " Built from "UTILITY_BUILD", project of "UTILITY_VENDOR, 3029 #endif 3030 "", 3031 "Usage:", 3032 " "UTILITY_NAME" -parse [OPTION] FILE", 3033 " "UTILITY_NAME" -scan [OPTION] FILE", 3034 " "UTILITY_NAME" -dump [OPTION]", 3035 "Description:", 3036 " Process unicode strings file.", 3037 "Common options include:", 3038 " -h or -? for this help information", 3039 " -db Database required name of output/input database file", 3040 " -bn BaseName for use in the .h and .c output files", 3041 " Default = "DEFAULT_BASE_NAME, 3042 " -v for verbose output", 3043 " -vdbw for verbose output when writing database", 3044 " -vdbr for verbose output when reading database", 3045 " -od FileName to specify an output database file name", 3046 "Parse options include:", 3047 " -i IncludePath add IncludePath to list of search paths", 3048 " -dep FileName to specify an output dependency file name", 3049 " -newdb to not read in existing database file", 3050 " -uqs to indicate that unquoted strings are used", 3051 " FileNames name of one or more unicode files to parse", 3052 "Scan options include:", 3053 " -scan scan text file(s) for STRING_TOKEN() usage", 3054 " -skipext .ext to skip scan of files with .ext filename extension", 3055 " -ppflag \"Flags\" to specify the C preprocessor flags", 3056 " -oh FileName to specify string defines file name for preprocessor", 3057 " -ignorenotfound ignore if a given STRING_TOKEN(STR) is not ", 3058 " found in the database", 3059 " FileNames one or more files to scan", 3060 "Dump options include:", 3061 " -oc FileName write string data to FileName", 3062 " -oh FileName write string defines to FileName", 3063 " -ou FileName dump database to unicode file FileName", 3064 " -lang Lang only dump for the language 'Lang'", 3065 " use ';' to separate multiple languages", 3066 " -if FileName to specify an indirection file", 3067 " -hpk FileName to create an HII export pack of the strings", 3068 "", 3069 "The expected process is to parse a unicode string file to create an initial", 3070 "database of string identifier names and string definitions. Then text files", 3071 "should be scanned for STRING_TOKEN() usages, and the referenced", 3072 "strings will be tagged as used in the database. After all files have been", 3073 "scanned, then the database should be dumped to create the necessary output", 3074 "files.", 3075 "", 3076 NULL 3077 }; 3078 for (Index = 0; Str[Index] != NULL; Index++) { 3079 fprintf (stdout, "%s\n", Str[Index]); 3080 } 3081 } 3082