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