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