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