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