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