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