f452bbc86bf0d9a4ad9054f6775f6c0e81b7f7b8
[mirror_edk2.git] / Tools / Source / TianoTools / StrGather / StringDB.c
1 /*++
2
3 Copyright (c) 2004, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 StringDB.c
15
16 Abstract:
17
18 String database implementation
19
20 --*/
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h> // for tolower()
26 #include <Base.h>
27 #include <UefiBaseTypes.h>
28 #include <MultiPhase.h>
29 #include "EfiUtilityMsgs.h"
30 #include "StrGather.h"
31 #include "StringDB.h"
32 #include "InternalFormRepresentation.h"
33
34 // #include EFI_PROTOCOL_DEFINITION (Hii)
35 #include <UgaDraw.h>
36 #include <Hii.h>
37
38 #define STRING_OFFSET RELOFST
39
40 #define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')
41 //
42 // Version supported by this tool
43 //
44 #define STRING_DB_VERSION 0x00010000
45
46 #define STRING_DB_MAJOR_VERSION_MASK 0xFFFF0000
47 #define STRING_DB_MINOR_VERSION_MASK 0x0000FFFF
48
49 #define DEFINE_STR L"// #define"
50
51 #define LANGUAGE_CODE_WIDTH 4
52 //
53 // This is the header that gets written to the top of the
54 // output binary database file.
55 //
56 typedef struct {
57 UINT32 Key;
58 UINT32 HeaderSize;
59 UINT32 Version;
60 UINT32 NumStringIdenfiers;
61 UINT32 StringIdentifiersSize;
62 UINT32 NumLanguages;
63 } STRING_DB_HEADER;
64
65 //
66 // When we write out data to the database, we have a UINT16 identifier, which
67 // indicates what follows, followed by the data. Here's the structure.
68 //
69 typedef struct {
70 UINT16 DataType;
71 UINT16 Reserved;
72 } DB_DATA_ITEM_HEADER;
73
74 #define DB_DATA_TYPE_INVALID 0x0000
75 #define DB_DATA_TYPE_STRING_IDENTIFIER 0x0001
76 #define DB_DATA_TYPE_LANGUAGE_DEFINITION 0x0002
77 #define DB_DATA_TYPE_STRING_DEFINITION 0x0003
78 #define DB_DATA_TYPE_LAST DB_DATA_TYPE_STRING_DEFINITION
79
80 //
81 // We have to keep track of a list of languages, each of which has its own
82 // list of strings. Define a structure to keep track of all languages and
83 // their list of strings.
84 //
85 typedef struct _STRING_LIST {
86 struct _STRING_LIST *Next;
87 UINT32 Size; // number of bytes in string, including null terminator
88 WCHAR *LanguageName;
89 WCHAR *StringName; // for example STR_ID_TEXT1
90 WCHAR *Scope; //
91 WCHAR *Str; // the actual string
92 UINT16 Flags; // properties of this string (used, undefined)
93 } STRING_LIST;
94
95 typedef struct _LANGUAGE_LIST {
96 struct _LANGUAGE_LIST *Next;
97 WCHAR LanguageName[4];
98 WCHAR *PrintableLanguageName;
99 STRING_LIST *String;
100 STRING_LIST *LastString;
101 } LANGUAGE_LIST;
102
103 //
104 // We also keep track of all the string identifier names, which we assign unique
105 // values to. Create a structure to keep track of them all.
106 //
107 typedef struct _STRING_IDENTIFIER {
108 struct _STRING_IDENTIFIER *Next;
109 UINT32 Index; // only need 16 bits, but makes it easier with UINT32
110 WCHAR *StringName;
111 UINT16 Flags; // if someone referenced it via STRING_TOKEN()
112 } STRING_IDENTIFIER;
113 //
114 // Keep our globals in this structure to be as modular as possible.
115 //
116 typedef struct {
117 FILE *StringDBFptr;
118 LANGUAGE_LIST *LanguageList;
119 LANGUAGE_LIST *LastLanguageList;
120 LANGUAGE_LIST *CurrentLanguage; // keep track of the last language they used
121 STRING_IDENTIFIER *StringIdentifier;
122 STRING_IDENTIFIER *LastStringIdentifier;
123 UINT8 *StringDBFileName;
124 UINT32 NumStringIdentifiers;
125 UINT32 NumStringIdentifiersReferenced;
126 STRING_IDENTIFIER *CurrentStringIdentifier; // keep track of the last string identifier they added
127 WCHAR *CurrentScope;
128 } STRING_DB_DATA;
129
130 static STRING_DB_DATA mDBData;
131
132 static const char *mSourceFileHeader[] = {
133 "//",
134 "// DO NOT EDIT -- auto-generated file",
135 "//",
136 "// This file is generated by the string gather utility",
137 "//",
138 NULL
139 };
140
141 static
142 STRING_LIST *
143 StringDBFindString (
144 WCHAR *LanguageName,
145 WCHAR *StringName,
146 WCHAR *Scope,
147 WCHAR_STRING_LIST *LanguagesOfInterest,
148 WCHAR_MATCHING_STRING_LIST *IndirectionList
149 );
150
151 static
152 STRING_IDENTIFIER *
153 StringDBFindStringIdentifierByName (
154 WCHAR *Name
155 );
156
157 static
158 STRING_IDENTIFIER *
159 StringDBFindStringIdentifierByIndex (
160 UINT32 Index
161 );
162
163 static
164 LANGUAGE_LIST *
165 StringDBFindLanguageList (
166 WCHAR *LanguageName
167 );
168
169 static
170 void
171 StringDBWriteStandardFileHeader (
172 FILE *OutFptr
173 );
174
175 static
176 WCHAR *
177 AsciiToWchar (
178 INT8 *Str
179 );
180
181 static
182 WCHAR *
183 DuplicateString (
184 WCHAR *Str
185 );
186
187 static
188 STATUS
189 StringDBWriteStringIdentifier (
190 FILE *DBFptr,
191 UINT16 StringId,
192 UINT16 Flags,
193 WCHAR *IdentifierName
194 );
195
196 static
197 STATUS
198 StringDBReadStringIdentifier (
199 FILE *DBFptr
200 );
201
202 static
203 STATUS
204 StringDBWriteLanguageDefinition (
205 FILE *DBFptr,
206 WCHAR *LanguageName,
207 WCHAR *PrintableLanguageName
208 );
209
210 static
211 STATUS
212 StringDBReadLanguageDefinition (
213 FILE *DBFptr
214 );
215
216 static
217 STATUS
218 StringDBWriteString (
219 FILE *DBFptr,
220 UINT16 Flags,
221 WCHAR *Language,
222 WCHAR *StringName,
223 WCHAR *Scope,
224 WCHAR *Str
225 );
226
227 static
228 STATUS
229 StringDBReadString (
230 FILE *DBFptr
231 );
232
233 static
234 STATUS
235 StringDBReadGenericString (
236 FILE *DBFptr,
237 UINT16 *Size,
238 WCHAR **Str
239 );
240
241 static
242 STATUS
243 StringDBWriteGenericString (
244 FILE *DBFptr,
245 WCHAR *Str
246 );
247
248 static
249 void
250 StringDBAssignStringIndexes (
251 VOID
252 );
253
254 /*****************************************************************************/
255
256 /*++
257
258 Routine Description:
259 Constructor function for the string database handler.
260
261 Arguments:
262 None.
263
264 Returns:
265 None.
266
267 --*/
268 void
269 StringDBConstructor (
270 VOID
271 )
272 {
273 memset ((char *) &mDBData, 0, sizeof (STRING_DB_DATA));
274 mDBData.CurrentScope = DuplicateString (L"NULL");
275 }
276
277 /*****************************************************************************/
278
279 /*++
280
281 Routine Description:
282 Destructor function for the string database handler.
283
284 Arguments:
285 None.
286
287 Returns:
288 None.
289
290 --*/
291 void
292 StringDBDestructor (
293 VOID
294 )
295 {
296 LANGUAGE_LIST *NextLang;
297 STRING_LIST *NextStr;
298 STRING_IDENTIFIER *NextIdentifier;
299 //
300 // Close the database file if it's open
301 //
302 if (mDBData.StringDBFptr != NULL) {
303 fclose (mDBData.StringDBFptr);
304 mDBData.StringDBFptr = NULL;
305 }
306 //
307 // If we've allocated any strings/languages, free them up
308 //
309 while (mDBData.LanguageList != NULL) {
310 NextLang = mDBData.LanguageList->Next;
311 //
312 // Free up all strings for this language
313 //
314 while (mDBData.LanguageList->String != NULL) {
315 NextStr = mDBData.LanguageList->String->Next;
316 FREE (mDBData.LanguageList->String->Str);
317 FREE (mDBData.LanguageList->String);
318 mDBData.LanguageList->String = NextStr;
319 }
320
321 FREE (mDBData.LanguageList->PrintableLanguageName);
322 FREE (mDBData.LanguageList);
323 mDBData.LanguageList = NextLang;
324 }
325 //
326 // Free up string identifiers
327 //
328 while (mDBData.StringIdentifier != NULL) {
329 NextIdentifier = mDBData.StringIdentifier->Next;
330 FREE (mDBData.StringIdentifier->StringName);
331 FREE (mDBData.StringIdentifier);
332 mDBData.StringIdentifier = NextIdentifier;
333 }
334 //
335 // Free the filename
336 //
337 if (mDBData.StringDBFileName != NULL) {
338 FREE (mDBData.StringDBFileName);
339 mDBData.StringDBFileName = NULL;
340 }
341 //
342 // We save a copy of the scope, so free it up if we
343 // have one.
344 //
345 if (mDBData.CurrentScope != NULL) {
346 FREE (mDBData.CurrentScope);
347 mDBData.CurrentScope = NULL;
348 }
349 }
350
351 /*****************************************************************************/
352
353 /*++
354
355 Routine Description:
356
357 Dump the contents of a database to an output C file.
358
359 Arguments:
360
361 FileName - name of the output file to write
362 BaseName - used for the name of the C array defined
363 Languages - list of languages of interest
364
365 Returns:
366
367 STATUS
368
369 Notes:
370
371 Languages is a pointer to a linked list of languages specified on
372 the command line. Format is "eng" and "spa+cat". For this, print
373 the strings for eng. Print the strings for spa too, but if one is
374 missing look for a cat string and print if it it exists.
375
376 --*/
377 STATUS
378 StringDBDumpCStrings (
379 INT8 *FileName,
380 INT8 *BaseName,
381 WCHAR_STRING_LIST *LanguagesOfInterest,
382 WCHAR_MATCHING_STRING_LIST *IndirectionList
383 )
384 {
385 FILE *Fptr;
386 LANGUAGE_LIST *Lang;
387 STRING_LIST *CurrString;
388 STRING_LIST EmptyString;
389 UINT32 Offset;
390 UINT32 StringIndex;
391 UINT32 TempIndex;
392 UINT32 BytesThisLine;
393 EFI_HII_STRING_PACK StringPack;
394 UINT8 *Ptr;
395 UINT32 Len;
396 WCHAR ZeroString[1];
397 WCHAR_STRING_LIST *LOIPtr;
398 BOOLEAN LanguageOk;
399 WCHAR *TempStringPtr;
400 WCHAR *LangName;
401 STRING_IDENTIFIER *StringIdentifier;
402
403 if ((Fptr = fopen (FileName, "w")) == NULL) {
404 Error (NULL, 0, 0, FileName, "failed to open output C string file");
405 return STATUS_ERROR;
406 }
407 //
408 // Assign index values to the string identifiers
409 //
410 StringDBAssignStringIndexes ();
411 //
412 // Write the standard header to the output file, then the structure
413 // definition header.
414 //
415 StringDBWriteStandardFileHeader (Fptr);
416 fprintf (Fptr, "\nunsigned char %s[] = {\n", BaseName);
417 //
418 // If a given string is not defined, then we'll use this one.
419 //
420 memset (&EmptyString, 0, sizeof (EmptyString));
421 EmptyString.Size = sizeof (ZeroString);
422 EmptyString.Str = ZeroString;
423 //
424 // Process each language, then each string for each langage
425 //
426 ZeroString[0] = 0;
427 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
428 //
429 // If we have a language list, then make sure this language is in that
430 // list.
431 //
432 LanguageOk = TRUE;
433 LangName = Lang->LanguageName;
434 if (LanguagesOfInterest != NULL) {
435 LanguageOk = FALSE;
436 for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) {
437 if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
438 LangName = LOIPtr->Str;
439 LanguageOk = TRUE;
440 break;
441 }
442 }
443 }
444
445 if (!LanguageOk) {
446 continue;
447 }
448 //
449 // Process each string for this language. We have to make 3 passes on the strings:
450 // Pass1: computes sizes and fill in the string pack header
451 // Pass2: write the array of offsets
452 // Pass3: write the strings
453 //
454 //
455 // PASS 1: Fill in and print the HII string pack header
456 //
457 // Compute the size for this language package and write
458 // the header out. Each string package contains:
459 // Header
460 // Offset[] -- an array of offsets to strings, of type RELOFST each
461 // String[] -- the actual strings themselves
462 //
463 fprintf (
464 Fptr,
465 "\n//******************************************************************************"
466 "\n// Start of string definitions for %S/%S",
467 Lang->LanguageName,
468 Lang->PrintableLanguageName
469 );
470 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK));
471 StringPack.Header.Type = EFI_HII_STRING;
472 StringPack.NumStringPointers = (UINT16) mDBData.NumStringIdentifiersReferenced;
473 //
474 // First string is the language name. If we're printing all languages, then
475 // it's just the "spa". If we were given a list of languages to print, then it's
476 // the "spacat" string. Compute its offset and fill in
477 // the info in the header. Since we know the language name string's length,
478 // and the printable language name follows it, use that info to fill in the
479 // entry for the printable language name as well.
480 //
481 StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));
482 StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));
483 //
484 // Add up the size of all strings so we can fill in our header.
485 //
486 Len = 0;
487 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
488 //
489 // For the first string (language name), we print out the "spacat" if they
490 // requested it. We set LangName to point to the proper language name string above.
491 //
492 if (StringIndex == STRING_ID_LANGUAGE_NAME) {
493 Len += (wcslen (LangName) + 1) * sizeof (WCHAR);
494 } else {
495 //
496 // Find a string with this language.stringname
497 //
498 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
499 if (StringIdentifier == NULL) {
500 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
501 return STATUS_ERROR;
502 }
503 //
504 // Find a matching string if this string identifier was referenced
505 //
506 EmptyString.Flags = STRING_FLAGS_UNDEFINED;
507 CurrString = NULL;
508 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
509 CurrString = StringDBFindString (
510 Lang->LanguageName,
511 StringIdentifier->StringName,
512 NULL,
513 LanguagesOfInterest,
514 IndirectionList
515 );
516 if (NULL == CurrString) {
517 //
518 // If string for Lang->LanguageName is not found, try to get an English version
519 //
520 CurrString = StringDBFindString (
521 L"eng",
522 StringIdentifier->StringName,
523 NULL,
524 LanguagesOfInterest,
525 IndirectionList
526 );
527 }
528 }
529
530 if (CurrString == NULL) {
531 CurrString = &EmptyString;
532 EmptyString.Flags |= StringIdentifier->Flags;
533 }
534
535 Len += CurrString->Size;
536 }
537 }
538 StringPack.Header.Length = sizeof (EFI_HII_STRING_PACK)
539 + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)
540 + Len;
541 //
542 // Write out the header one byte at a time
543 //
544 Ptr = (UINT8 *) &StringPack;
545 for (TempIndex = 0; TempIndex < sizeof (EFI_HII_STRING_PACK); TempIndex++, Ptr++) {
546 if ((TempIndex & 0x07) == 0) {
547 fprintf (Fptr, "\n ");
548 }
549
550 fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);
551 }
552
553 fprintf (Fptr, "\n // offset 0x%X\n", sizeof (StringPack));
554 //
555 // PASS2 : write the offsets
556 //
557 // Traverse the list of strings again and write the array of offsets. The
558 // offset to the first string is the size of the string pack header
559 // plus the size of the offsets array. The other strings follow it.
560 //
561 StringIndex = 0;
562 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
563 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
564 //
565 // Write the offset, followed by a useful comment
566 //
567 fprintf (Fptr, " ");
568 Ptr = (UINT8 *) &Offset;
569 for (TempIndex = 0; TempIndex < sizeof (STRING_OFFSET); TempIndex++) {
570 fprintf (Fptr, "0x%02X, ", (UINT32) Ptr[TempIndex]);
571 }
572 //
573 // Find the string name
574 //
575 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
576 if (StringIdentifier == NULL) {
577 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
578 return STATUS_ERROR;
579 }
580
581 fprintf (Fptr, " // offset to string %S (0x%04X)", StringIdentifier->StringName, StringIndex);
582 //
583 // For the first string (language name), we print out the "spacat" if they
584 // requested it. We set LangName to point to the proper language name string above.
585 //
586 if (StringIndex == STRING_ID_LANGUAGE_NAME) {
587 Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);
588 CurrString = StringDBFindString (
589 Lang->LanguageName,
590 StringIdentifier->StringName,
591 NULL, // scope
592 NULL,
593 NULL
594 );
595 } else {
596 //
597 // Find a matching string
598 //
599 CurrString = StringDBFindString (
600 Lang->LanguageName,
601 StringIdentifier->StringName,
602 NULL, // scope
603 LanguagesOfInterest,
604 IndirectionList
605 );
606
607 if (NULL == CurrString) {
608 CurrString = StringDBFindString (
609 L"eng",
610 StringIdentifier->StringName,
611 NULL, // scope
612 LanguagesOfInterest,
613 IndirectionList
614 );
615 }
616
617 EmptyString.LanguageName = Lang->LanguageName;
618 if (CurrString == NULL) {
619 CurrString = &EmptyString;
620 EmptyString.Flags = STRING_FLAGS_UNDEFINED;
621 } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
622 CurrString = &EmptyString;
623 EmptyString.Flags = 0;
624 }
625
626 Offset += CurrString->Size;
627 }
628 //
629 // Print useful info about this string
630 //
631 if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
632 fprintf (Fptr, " - not referenced");
633 }
634
635 if (CurrString->Flags & STRING_FLAGS_UNDEFINED) {
636 fprintf (Fptr, " - not defined for this language");
637 } else if (wcscmp (CurrString->LanguageName, Lang->LanguageName) != 0) {
638 fprintf (
639 Fptr,
640 " - not defined for this language -- using secondary language %S definition",
641 CurrString->LanguageName
642 );
643 }
644
645 fprintf (Fptr, "\n");
646 }
647 //
648 // For unreferenced string identifiers, print a message that they are not referenced anywhere
649 //
650 while (StringIndex < mDBData.NumStringIdentifiers) {
651 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
652 if (StringIdentifier != NULL) {
653 fprintf (Fptr, " // %S not referenced\n", StringIdentifier->StringName);
654 }
655
656 StringIndex++;
657 }
658
659 //
660 // PASS 3: write the strings themselves.
661 // Keep track of how many bytes we write per line because some editors
662 // (Visual Studio for instance) can't handle too long of lines.
663 //
664 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
665 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
666 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
667 if (StringIdentifier == NULL) {
668 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
669 return STATUS_ERROR;
670 }
671
672 fprintf (Fptr, " // string %S offset 0x%08X\n ", StringIdentifier->StringName, Offset);
673 //
674 // For the first string (language name), we print out the "spacat" if they
675 // requested it. We set LangName to point to the proper language name string above.
676 //
677 if (StringIndex == STRING_ID_LANGUAGE_NAME) {
678 TempStringPtr = LangName;
679 } else {
680 //
681 // Find a matching string if this string identifier was referenced
682 //
683 CurrString = NULL;
684 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
685 CurrString = StringDBFindString (
686 Lang->LanguageName,
687 StringIdentifier->StringName,
688 NULL, // scope
689 LanguagesOfInterest,
690 IndirectionList
691 );
692 if (NULL == CurrString) {
693 CurrString = StringDBFindString (
694 L"eng",
695 StringIdentifier->StringName,
696 NULL, // scope
697 LanguagesOfInterest,
698 IndirectionList
699 );
700 }
701 }
702
703 if (CurrString == NULL) {
704 CurrString = &EmptyString;
705 }
706
707 TempStringPtr = CurrString->Str;
708 }
709
710 BytesThisLine = 0;
711 for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {
712 fprintf (
713 Fptr,
714 "0x%02X, 0x%02X, ",
715 (UINT32) TempStringPtr[TempIndex] & 0xFF,
716 (UINT32) ((TempStringPtr[TempIndex] >> 8) & 0xFF)
717 );
718 BytesThisLine += 2;
719 Offset += 2;
720 //
721 // Let's say we only allow 14 per line
722 //
723 if (BytesThisLine > 14) {
724 fprintf (Fptr, "\n ");
725 BytesThisLine = 0;
726 }
727 }
728 //
729 // Print NULL WCHAR at the end of this string.
730 //
731 fprintf (Fptr, "0x00, 0x00,\n");
732 Offset += 2;
733 }
734 //
735 // Sanity check the offset. Make sure our running offset is what we put in the
736 // string pack header.
737 //
738 if (StringPack.Header.Length != Offset) {
739 Error (
740 __FILE__,
741 __LINE__,
742 0,
743 "application error",
744 "stringpack size 0x%X does not match final size 0x%X",
745 StringPack.Header.Length,
746 Offset
747 );
748 }
749 }
750 //
751 // Print terminator string pack, closing brace and close the file.
752 // The size of 0 triggers to the consumer that this is the end.
753 //
754 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK));
755 StringPack.Header.Type = EFI_HII_STRING;
756 Ptr = (UINT8 *) &StringPack;
757 fprintf (Fptr, "\n // strings terminator pack");
758 for (TempIndex = 0; TempIndex < sizeof (StringPack); TempIndex++, Ptr++) {
759 if ((TempIndex & 0x0F) == 0) {
760 fprintf (Fptr, "\n ");
761 }
762
763 fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);
764 }
765
766 fprintf (Fptr, "\n};\n");
767 fclose (Fptr);
768 return STATUS_SUCCESS;
769 }
770
771 /*****************************************************************************/
772
773 /*++
774
775 Routine Description:
776
777 Dump the #define string names
778
779 Arguments:
780
781 FileName - name of the output file to write
782 BaseName - used for the protection #ifndef/#endif
783
784 Returns:
785
786 STATUS
787
788 --*/
789 STATUS
790 StringDBDumpStringDefines (
791 INT8 *FileName,
792 INT8 *BaseName
793 )
794 {
795 FILE *Fptr;
796 STRING_IDENTIFIER *Identifier;
797 INT8 CopyBaseName[100];
798 UINT32 Index;
799 const INT8 *StrDefHeader[] = {
800 "#ifndef _%s_STRINGS_DEFINE_H_\n",
801 "#define _%s_STRINGS_DEFINE_H_\n\n",
802 NULL
803 };
804
805 if ((Fptr = fopen (FileName, "w")) == NULL) {
806 Error (NULL, 0, 0, FileName, "failed to open output string defines file");
807 return STATUS_ERROR;
808 }
809 //
810 // Get the base source filename and convert to uppercase.
811 //
812 if (sizeof (CopyBaseName) <= strlen (BaseName) + 1) {
813 Error (NULL, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");
814 return STATUS_ERROR;
815 }
816
817 strcpy (CopyBaseName, BaseName);
818 for (Index = 0; CopyBaseName[Index] != 0; Index++) {
819 if (islower (CopyBaseName[Index])) {
820 CopyBaseName[Index] = (INT8) toupper (CopyBaseName[Index]);
821 }
822 }
823 //
824 // Assign index values to the string identifiers
825 //
826 StringDBAssignStringIndexes ();
827 //
828 // Write the standard header to the output file, and then the
829 // protective #ifndef.
830 //
831 StringDBWriteStandardFileHeader (Fptr);
832 for (Index = 0; StrDefHeader[Index] != NULL; Index++) {
833 fprintf (Fptr, StrDefHeader[Index], CopyBaseName);
834 }
835 //
836 // Print all the #defines for the string identifiers. Print identifiers
837 // whose names start with '$' as comments. Add comments for string
838 // identifiers not used as well.
839 //
840 Identifier = mDBData.StringIdentifier;
841 while (Identifier != NULL) {
842 if (Identifier->StringName[0] == L'$') {
843 fprintf (Fptr, "// ");
844 }
845
846 if (Identifier->Flags & STRING_FLAGS_REFERENCED) {
847 fprintf (Fptr, "#define %-40S 0x%04X\n", Identifier->StringName, Identifier->Index);
848 } else {
849 fprintf (Fptr, "//#define %-40S 0x%04X // not referenced\n", Identifier->StringName, Identifier->Index);
850 }
851
852 Identifier = Identifier->Next;
853 }
854
855 fprintf (Fptr, "\n#endif\n");
856 fclose (Fptr);
857 return STATUS_SUCCESS;
858 }
859
860 /*****************************************************************************/
861
862 /*++
863
864 Routine Description:
865
866 Add a string identifier to the database.
867
868 Arguments:
869
870 StringName - name of the string identifier. For example "STR_MY_STRING"
871 NewId - if an ID has been assigned
872 Flags - characteristics for the identifier
873
874 Returns:
875
876 STATUS
877
878 --*/
879 STATUS
880 StringDBAddStringIdentifier (
881 WCHAR *StringName,
882 UINT16 *NewId,
883 UINT16 Flags
884 )
885 {
886 STRING_IDENTIFIER *StringIdentifier;
887 STATUS Status;
888 //
889 // If it was already used for some other language, then we don't
890 // need to add it. But set it to the current string identifier.
891 // The referenced bit is sticky.
892 //
893 Status = STATUS_SUCCESS;
894 StringIdentifier = StringDBFindStringIdentifierByName (StringName);
895 if (StringIdentifier != NULL) {
896 if (Flags & STRING_FLAGS_REFERENCED) {
897 StringIdentifier->Flags |= STRING_FLAGS_REFERENCED;
898 }
899
900 mDBData.CurrentStringIdentifier = StringIdentifier;
901 *NewId = (UINT16) StringIdentifier->Index;
902 return Status;
903 }
904
905 StringIdentifier = (STRING_IDENTIFIER *) MALLOC (sizeof (STRING_IDENTIFIER));
906 if (StringIdentifier == NULL) {
907 Error (NULL, 0, 0, NULL, "memory allocation error");
908 return STATUS_ERROR;
909 }
910
911 memset ((char *) StringIdentifier, 0, sizeof (STRING_IDENTIFIER));
912 StringIdentifier->StringName = (WCHAR *) malloc ((wcslen (StringName) + 1) * sizeof (WCHAR));
913 if (StringIdentifier->StringName == NULL) {
914 Error (NULL, 0, 0, NULL, "memory allocation error");
915 return STATUS_ERROR;
916 }
917
918 wcscpy (StringIdentifier->StringName, StringName);
919 if (*NewId != STRING_ID_INVALID) {
920 StringIdentifier->Index = *NewId;
921 StringIdentifier->Flags |= STRING_FLAGS_INDEX_ASSIGNED;
922 if (mDBData.NumStringIdentifiers <= StringIdentifier->Index) {
923 mDBData.NumStringIdentifiers = StringIdentifier->Index + 1;
924 }
925 } else {
926 StringIdentifier->Index = mDBData.NumStringIdentifiers++;
927 }
928
929 StringIdentifier->Flags |= Flags;
930 //
931 // Add it to our list of string identifiers
932 //
933 if (mDBData.StringIdentifier == NULL) {
934 mDBData.StringIdentifier = StringIdentifier;
935 } else {
936 mDBData.LastStringIdentifier->Next = StringIdentifier;
937 }
938
939 mDBData.LastStringIdentifier = StringIdentifier;
940 mDBData.CurrentStringIdentifier = StringIdentifier;
941 *NewId = (UINT16) StringIdentifier->Index;
942 return Status;
943 }
944
945 /*****************************************************************************/
946
947 /*++
948
949 Routine Description:
950
951 Add a new string to the database.
952
953 Arguments:
954
955 LanguageName - "eng" or "spa" language name
956 StringName - "STR_MY_TEXT" string name
957 Scope - from the #scope statements in the string file
958 Format - if we should format the string
959 Flags - characteristic flags for the string
960
961 Returns:
962
963 STATUS
964
965 Notes:
966
967 Several of the fields can be "inherited" from the previous calls to
968 our database functions. For example, if scope is NULL here, then
969 we'll use the previous setting.
970
971 --*/
972 STATUS
973 StringDBAddString (
974 WCHAR *LanguageName,
975 WCHAR *StringName,
976 WCHAR *Scope,
977 WCHAR *String,
978 BOOLEAN Format,
979 UINT16 Flags
980 )
981 {
982 LANGUAGE_LIST *Lang;
983 UINT32 Size;
984 STRING_LIST *Str;
985 UINT16 StringIndex;
986 WCHAR TempLangName[4];
987 STRING_IDENTIFIER *StringIdentifier;
988
989 //
990 // Check that language name is exactly 3 characters, or emit an error.
991 // Truncate at 3 if it's longer, or make it 3 if it's shorter.
992 //
993 if (LanguageName != NULL) {
994 Size = wcslen (LanguageName);
995 if (Size != 3) {
996 ParserError (0, "invalid length for language name", "%S", LanguageName);
997 if (Size > 3) {
998 LanguageName[3] = 0;
999 } else {
1000 //
1001 // Make a local copy of the language name string, and extend to
1002 // 3 characters since we make assumptions elsewhere in this program
1003 // on the length.
1004 //
1005 wcscpy (TempLangName, LanguageName);
1006 for (; Size < 3; Size++) {
1007 TempLangName[Size] = L'?';
1008 }
1009
1010 TempLangName[4] = 0;
1011 LanguageName = TempLangName;
1012 }
1013 }
1014 }
1015 //
1016 // If they specified a language, make sure they've defined it already
1017 // via a #langdef statement. Otherwise use the current default language.
1018 //
1019 if (LanguageName != NULL) {
1020 Lang = StringDBFindLanguageList (LanguageName);
1021 if (Lang == NULL) {
1022 ParserError (0, "language not defined", "%S", LanguageName);
1023 return STATUS_ERROR;
1024 } else {
1025 StringDBSetCurrentLanguage (LanguageName);
1026 }
1027 } else {
1028 Lang = mDBData.CurrentLanguage;
1029 if (Lang == NULL) {
1030 //
1031 // Have to call SetLanguage() first
1032 //
1033 ParserError (0, "no language defined", "%S", StringName);
1034 return STATUS_ERROR;
1035 }
1036 }
1037 //
1038 // If they didn't define a string identifier, use the last string identifier
1039 // added.
1040 //
1041 if (StringName == NULL) {
1042 StringName = mDBData.CurrentStringIdentifier->StringName;
1043 if (StringName == NULL) {
1044 ParserError (0, "no string identifier previously specified", NULL);
1045 return STATUS_ERROR;
1046 }
1047 }
1048 //
1049 // If scope was not specified, use the default setting
1050 //
1051 if (Scope != NULL) {
1052 Scope = DuplicateString (Scope);
1053 } else {
1054 Scope = DuplicateString (mDBData.CurrentScope);
1055 }
1056 //
1057 // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);
1058 //
1059 // Check for duplicates for this Language.StringName.Scope. Allow multiple
1060 // definitions of the language name and printable language name, since the
1061 // user does not specifically define them.
1062 //
1063 if (StringDBFindString (Lang->LanguageName, StringName, Scope, NULL, NULL) != NULL) {
1064 if ((wcscmp (StringName, LANGUAGE_NAME_STRING_NAME) == 0) &&
1065 (wcscmp (StringName, PRINTABLE_LANGUAGE_NAME_STRING_NAME) == 0)
1066 ) {
1067 ParserError (
1068 0,
1069 "string multiply defined",
1070 "Language.Name.Scope = %S.%S.%S",
1071 Lang->LanguageName,
1072 StringName,
1073 Scope
1074 );
1075 return STATUS_ERROR;
1076 }
1077 }
1078
1079 StringIndex = STRING_ID_INVALID;
1080 if (StringDBAddStringIdentifier (StringName, &StringIndex, Flags) != STATUS_SUCCESS) {
1081 return STATUS_ERROR;
1082 }
1083
1084 StringIdentifier = StringDBFindStringIdentifierByName (StringName);
1085 //
1086 // Add this string to the end of the strings for this language.
1087 //
1088 Str = (STRING_LIST *) malloc (sizeof (STRING_LIST));
1089 if (Str == NULL) {
1090 Error (NULL, 0, 0, NULL, "memory allocation error");
1091 return STATUS_ERROR;
1092 }
1093
1094 memset ((char *) Str, 0, sizeof (STRING_LIST));
1095 Size = (wcslen (String) + 1) * sizeof (WCHAR);
1096 Str->Flags = Flags;
1097 Str->Scope = Scope;
1098 Str->StringName = StringIdentifier->StringName;
1099 Str->LanguageName = DuplicateString (LanguageName);
1100 Str->Str = (WCHAR *) MALLOC (Size);
1101 if (Str->Str == NULL) {
1102 Error (NULL, 0, 0, NULL, "memory allocation error");
1103 return STATUS_ERROR;
1104 }
1105 //
1106 // If not formatting, just copy the string.
1107 //
1108 wcscpy (Str->Str, String);
1109 if (Format) {
1110 StringDBFormatString (Str->Str);
1111 }
1112 //
1113 // Size may change after formatting. We set the size to
1114 // the actual size of the string, including the null for
1115 // easier processing later.
1116 //
1117 Str->Size = (wcslen (Str->Str) + 1) * sizeof (WCHAR);
1118 if (Lang->String == NULL) {
1119 Lang->String = Str;
1120 } else {
1121 Lang->LastString->Next = Str;
1122 }
1123
1124 Lang->LastString = Str;
1125 return STATUS_SUCCESS;
1126 }
1127
1128 /*****************************************************************************/
1129
1130 /*++
1131
1132 Routine Description:
1133
1134 Given a language name, see if a language list for it has been defined
1135
1136 Arguments:
1137
1138 LanguageName - like "eng"
1139
1140 Returns:
1141
1142 A pointer to the language list
1143
1144 --*/
1145 static
1146 LANGUAGE_LIST *
1147 StringDBFindLanguageList (
1148 WCHAR *LanguageName
1149 )
1150 {
1151 LANGUAGE_LIST *Lang;
1152
1153 Lang = mDBData.LanguageList;
1154 while (Lang != NULL) {
1155 if (wcscmp (LanguageName, Lang->LanguageName) == 0) {
1156 break;
1157 }
1158
1159 Lang = Lang->Next;
1160 }
1161
1162 return Lang;
1163 }
1164
1165 /*****************************************************************************/
1166 STATUS
1167 StringDBSetCurrentLanguage (
1168 WCHAR *LanguageName
1169 )
1170 {
1171 LANGUAGE_LIST *Lang;
1172
1173 Lang = StringDBFindLanguageList (LanguageName);
1174 if (Lang == NULL) {
1175 ParserError (0, "language not previously defined", "%S", LanguageName);
1176 return STATUS_ERROR;
1177 }
1178
1179 mDBData.CurrentLanguage = Lang;
1180 return STATUS_SUCCESS;
1181 }
1182
1183 /*****************************************************************************/
1184 STATUS
1185 StringDBAddLanguage (
1186 WCHAR *LanguageName,
1187 WCHAR *PrintableLanguageName
1188 )
1189 {
1190 LANGUAGE_LIST *Lang;
1191 //
1192 // Check for redefinitions
1193 //
1194 Lang = StringDBFindLanguageList (LanguageName);
1195 if (Lang != NULL) {
1196 //
1197 // Better be the same printable name
1198 //
1199 if (wcscmp (PrintableLanguageName, Lang->PrintableLanguageName) != 0) {
1200 ParserError (
1201 0,
1202 "language redefinition",
1203 "%S:%S != %S:%S",
1204 Lang->LanguageName,
1205 Lang->PrintableLanguageName,
1206 LanguageName,
1207 PrintableLanguageName
1208 );
1209 return STATUS_ERROR;
1210 //
1211 // } else {
1212 // ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);
1213 // return STATUS_WARNING;
1214 //
1215 }
1216 } else {
1217 //
1218 // Allocate memory to keep track of this new language
1219 //
1220 Lang = (LANGUAGE_LIST *) malloc (sizeof (LANGUAGE_LIST));
1221 if (Lang == NULL) {
1222 Error (NULL, 0, 0, NULL, "memory allocation error");
1223 return STATUS_ERROR;
1224 }
1225
1226 memset ((char *) Lang, 0, sizeof (LANGUAGE_LIST));
1227 //
1228 // Save the language name, then allocate memory to save the
1229 // printable language name
1230 //
1231 wcscpy (Lang->LanguageName, LanguageName);
1232 Lang->PrintableLanguageName = (WCHAR *) malloc ((wcslen (PrintableLanguageName) + 1) * sizeof (WCHAR));
1233 if (Lang->PrintableLanguageName == NULL) {
1234 Error (NULL, 0, 0, NULL, "memory allocation error");
1235 return STATUS_ERROR;
1236 }
1237
1238 wcscpy (Lang->PrintableLanguageName, PrintableLanguageName);
1239
1240 if (mDBData.LanguageList == NULL) {
1241 mDBData.LanguageList = Lang;
1242 } else {
1243 mDBData.LastLanguageList->Next = Lang;
1244 }
1245
1246 mDBData.LastLanguageList = Lang;
1247 }
1248 //
1249 // Default is to make our active language this new one
1250 //
1251 StringDBSetCurrentLanguage (LanguageName);
1252 //
1253 // The first two strings for any language are the language name,
1254 // followed by the printable language name. Add them and set them
1255 // to referenced so they never get stripped out.
1256 //
1257 StringDBAddString (
1258 LanguageName,
1259 LANGUAGE_NAME_STRING_NAME,
1260 NULL,
1261 LanguageName,
1262 FALSE,
1263 STRING_FLAGS_REFERENCED
1264 );
1265 StringDBAddString (
1266 LanguageName,
1267 PRINTABLE_LANGUAGE_NAME_STRING_NAME,
1268 NULL,
1269 PrintableLanguageName,
1270 FALSE,
1271 STRING_FLAGS_REFERENCED
1272 );
1273 return STATUS_SUCCESS;
1274 }
1275
1276 /*****************************************************************************/
1277 static
1278 STRING_IDENTIFIER *
1279 StringDBFindStringIdentifierByName (
1280 WCHAR *StringName
1281 )
1282 {
1283 STRING_IDENTIFIER *Identifier;
1284
1285 Identifier = mDBData.StringIdentifier;
1286 while (Identifier != NULL) {
1287 if (wcscmp (StringName, Identifier->StringName) == 0) {
1288 return Identifier;
1289 }
1290
1291 Identifier = Identifier->Next;
1292 }
1293
1294 return NULL;
1295 }
1296
1297 static
1298 STRING_IDENTIFIER *
1299 StringDBFindStringIdentifierByIndex (
1300 UINT32 StringIndex
1301 )
1302 {
1303 STRING_IDENTIFIER *Identifier;
1304
1305 Identifier = mDBData.StringIdentifier;
1306 while (Identifier != NULL) {
1307 if (Identifier->Index == StringIndex) {
1308 return Identifier;
1309 }
1310
1311 Identifier = Identifier->Next;
1312 }
1313
1314 return NULL;
1315 }
1316
1317 /*****************************************************************************/
1318 static
1319 void
1320 StringDBWriteStandardFileHeader (
1321 FILE *OutFptr
1322 )
1323 {
1324 UINT32 TempIndex;
1325 for (TempIndex = 0; mSourceFileHeader[TempIndex] != NULL; TempIndex++) {
1326 fprintf (OutFptr, "%s\n", mSourceFileHeader[TempIndex]);
1327 }
1328 }
1329
1330 /*****************************************************************************/
1331
1332 /*++
1333
1334 Routine Description:
1335
1336 Given a Unicode string from an input file, reformat the string to replace
1337 backslash control sequences with the appropriate encoding.
1338
1339 Arguments:
1340
1341 String - pointer to string to reformat
1342
1343 Returns:
1344
1345 Nothing
1346
1347 --*/
1348 void
1349 StringDBFormatString (
1350 WCHAR *String
1351 )
1352 {
1353 WCHAR *From;
1354 WCHAR *To;
1355 int HexNibbles;
1356 WCHAR HexValue;
1357 //
1358 // Go through the string and process any formatting characters
1359 //
1360 From = String;
1361 To = String;
1362 while (*From) {
1363 if (*From == UNICODE_BACKSLASH) {
1364 //
1365 // First look for \wide and replace with the appropriate control character. Note that
1366 // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is
1367 // counted. Make adjustments for this. We advance From below, so subtract 2 each time.
1368 //
1369 if (wcsncmp (From, UNICODE_WIDE_STRING, sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 1) == 0) {
1370 *To = WIDE_CHAR;
1371 From += sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 2;
1372 } else if (wcsncmp (From, UNICODE_NARROW_STRING, sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 1) == 0) {
1373 //
1374 // Found: \narrow
1375 //
1376 *To = NARROW_CHAR;
1377 From += sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 2;
1378 } else if (wcsncmp (From, UNICODE_NBR_STRING, sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 1) == 0) {
1379 //
1380 // Found: \nbr
1381 //
1382 *To = NON_BREAKING_CHAR;
1383 From += sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 2;
1384 } else if (wcsncmp (From, UNICODE_BR_STRING, sizeof (UNICODE_BR_STRING) / sizeof (WCHAR) - 1) == 0) {
1385 //
1386 // Found: \br -- pass through untouched
1387 //
1388 *To = *From;
1389 } else {
1390 //
1391 // Standard one-character control sequences such as \n, \r, \\, or \x
1392 //
1393 From++;
1394 switch (*From) {
1395 case ASCII_TO_UNICODE ('n'):
1396 *To = UNICODE_CR;
1397 To++;
1398 *To = UNICODE_LF;
1399 break;
1400
1401 //
1402 // carriage return
1403 //
1404 case ASCII_TO_UNICODE ('r'):
1405 *To = UNICODE_CR;
1406 break;
1407
1408 //
1409 // backslash
1410 //
1411 case UNICODE_BACKSLASH:
1412 *To = UNICODE_BACKSLASH;
1413 break;
1414
1415 //
1416 // Tab
1417 //
1418 case ASCII_TO_UNICODE ('t'):
1419 *To = UNICODE_TAB;
1420 break;
1421
1422 //
1423 // embedded double-quote
1424 //
1425 case UNICODE_DOUBLE_QUOTE:
1426 *To = UNICODE_DOUBLE_QUOTE;
1427 break;
1428
1429 //
1430 // Hex Unicode character \x1234. We'll process up to 4 hex characters
1431 //
1432 case ASCII_TO_UNICODE ('x'):
1433 HexValue = 0;
1434 for (HexNibbles = 0; HexNibbles < 4; HexNibbles++) {
1435 if ((From[1] >= UNICODE_0) && (From[1] <= UNICODE_9)) {
1436 HexValue = (HexValue << 4) | (From[1] - UNICODE_0);
1437 } else if ((From[1] >= UNICODE_a) && (From[1] <= UNICODE_f)) {
1438 HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_a);
1439 } else if ((From[1] >= UNICODE_A) && (From[1] <= UNICODE_F)) {
1440 HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_A);
1441 } else {
1442 break;
1443 }
1444
1445 From++;
1446 }
1447
1448 if (HexNibbles == 0) {
1449 ParserWarning (
1450 0,
1451 "expected at least one valid hex digit with \\x escaped character in string",
1452 "\\%C",
1453 *From
1454 );
1455 } else {
1456 *To = HexValue;
1457 }
1458 break;
1459
1460 default:
1461 *To = UNICODE_SPACE;
1462 ParserWarning (0, "invalid escaped character in string", "\\%C", *From);
1463 break;
1464 }
1465 }
1466 } else {
1467 *To = *From;
1468 }
1469
1470 From++;
1471 To++;
1472 }
1473
1474 *To = 0;
1475 }
1476
1477 /*****************************************************************************/
1478 STATUS
1479 StringDBReadDatabase (
1480 INT8 *DBFileName,
1481 BOOLEAN IgnoreIfNotExist,
1482 BOOLEAN Verbose
1483 )
1484 {
1485 STRING_DB_HEADER DbHeader;
1486 STATUS Status;
1487 FILE *DBFptr;
1488 DB_DATA_ITEM_HEADER DataItemHeader;
1489
1490 Status = STATUS_SUCCESS;
1491 DBFptr = NULL;
1492 //
1493 // if (Verbose) {
1494 // fprintf (stdout, "Reading database file %s\n", DBFileName);
1495 // }
1496 //
1497 // Try to open the input file
1498 //
1499 if ((DBFptr = fopen (DBFileName, "rb")) == NULL) {
1500 if (IgnoreIfNotExist) {
1501 return STATUS_SUCCESS;
1502 }
1503
1504 Error (NULL, 0, 0, DBFileName, "failed to open input database file for reading");
1505 return STATUS_ERROR;
1506 }
1507 //
1508 // Read and verify the database header
1509 //
1510 if (fread ((void *) &DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr) != 1) {
1511 Error (NULL, 0, 0, DBFileName, "failed to read header from database file");
1512 Status = STATUS_ERROR;
1513 goto Finish;
1514 }
1515
1516 if (DbHeader.Key != STRING_DB_KEY) {
1517 Error (NULL, 0, 0, DBFileName, "invalid header in database file");
1518 Status = STATUS_ERROR;
1519 goto Finish;
1520 }
1521
1522 if ((DbHeader.Version & STRING_DB_MAJOR_VERSION_MASK) != (STRING_DB_VERSION & STRING_DB_MAJOR_VERSION_MASK)) {
1523 Error (NULL, 0, 0, DBFileName, "incompatible database file version -- rebuild clean");
1524 Status = STATUS_ERROR;
1525 goto Finish;
1526 }
1527 //
1528 // Read remaining items
1529 //
1530 while (fread (&DataItemHeader, sizeof (DataItemHeader), 1, DBFptr) == 1) {
1531 switch (DataItemHeader.DataType) {
1532 case DB_DATA_TYPE_STRING_IDENTIFIER:
1533 StringDBReadStringIdentifier (DBFptr);
1534 break;
1535
1536 case DB_DATA_TYPE_LANGUAGE_DEFINITION:
1537 StringDBReadLanguageDefinition (DBFptr);
1538 break;
1539
1540 case DB_DATA_TYPE_STRING_DEFINITION:
1541 StringDBReadString (DBFptr);
1542 break;
1543
1544 default:
1545 Error (
1546 NULL,
1547 0,
1548 0,
1549 "database corrupted",
1550 "invalid data item type 0x%X at offset 0x%X",
1551 (UINT32) DataItemHeader.DataType,
1552 ftell (DBFptr) - sizeof (DataItemHeader)
1553 );
1554 Status = STATUS_ERROR;
1555 goto Finish;
1556 }
1557 }
1558
1559 Finish:
1560 if (DBFptr != NULL) {
1561 fclose (DBFptr);
1562 }
1563
1564 return Status;
1565 }
1566
1567 /*****************************************************************************/
1568
1569 /*++
1570
1571 Routine Description:
1572
1573 Write everything we know to the output database file. Write:
1574
1575 Database header
1576 String identifiers[]
1577 StringPacks[]
1578
1579 Arguments:
1580
1581 DBFileName - name of the file to write to
1582 Verbose - for debug purposes, print info messages along the way.
1583
1584 Returns:
1585
1586 STATUS
1587
1588 --*/
1589 STATUS
1590 StringDBWriteDatabase (
1591 INT8 *DBFileName,
1592 BOOLEAN Verbose
1593 )
1594 {
1595 STRING_DB_HEADER DbHeader;
1596 UINT32 Counter;
1597 UINT32 StrLen;
1598 LANGUAGE_LIST *Lang;
1599 STRING_IDENTIFIER *StringIdentifier;
1600 STRING_LIST *StrList;
1601 FILE *DBFptr;
1602
1603 if (Verbose) {
1604 fprintf (stdout, "Writing database %s\n", DBFileName);
1605 }
1606
1607 if ((DBFptr = fopen (DBFileName, "wb")) == NULL) {
1608 Error (NULL, 0, 0, DBFileName, "failed to open output database file for writing");
1609 return STATUS_ERROR;
1610 }
1611 //
1612 // Fill in and write the database header
1613 //
1614 memset (&DbHeader, 0, sizeof (STRING_DB_HEADER));
1615 DbHeader.HeaderSize = sizeof (STRING_DB_HEADER);
1616 DbHeader.Key = STRING_DB_KEY;
1617 DbHeader.Version = STRING_DB_VERSION;
1618 //
1619 // Count the number of languages we have
1620 //
1621 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
1622 DbHeader.NumLanguages++;
1623 }
1624 //
1625 // Count up how many string identifiers we have, and total up the
1626 // size of the names plus the size of the flags field we will
1627 // write out too.
1628 //
1629 DbHeader.NumStringIdenfiers = mDBData.NumStringIdentifiers;
1630 StringIdentifier = mDBData.StringIdentifier;
1631 for (Counter = 0; Counter < mDBData.NumStringIdentifiers; Counter++) {
1632 StrLen = wcslen (StringIdentifier->StringName) + 1;
1633 DbHeader.StringIdentifiersSize += StrLen * sizeof (WCHAR) + sizeof (StringIdentifier->Flags);
1634 StringIdentifier = StringIdentifier->Next;
1635 }
1636
1637 //
1638 // Write the header
1639 //
1640 fwrite (&DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr);
1641 if (Verbose) {
1642 fprintf (stdout, " Number of string identifiers 0x%04X\n", DbHeader.NumStringIdenfiers);
1643 fprintf (stdout, " Number of languages %d\n", DbHeader.NumLanguages);
1644 }
1645 //
1646 // Write the string identifiers
1647 //
1648 for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {
1649 StringDBWriteStringIdentifier (
1650 DBFptr,
1651 (UINT16) StringIdentifier->Index,
1652 StringIdentifier->Flags,
1653 StringIdentifier->StringName
1654 );
1655 }
1656 //
1657 // Now write all the strings for each language
1658 //
1659 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
1660 StringDBWriteLanguageDefinition (DBFptr, Lang->LanguageName, Lang->PrintableLanguageName);
1661 for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {
1662 StringDBWriteString (
1663 DBFptr,
1664 StrList->Flags,
1665 Lang->LanguageName,
1666 StrList->StringName,
1667 StrList->Scope,
1668 StrList->Str
1669 );
1670 }
1671 }
1672
1673 fclose (DBFptr);
1674 return STATUS_SUCCESS;
1675 }
1676
1677 STATUS
1678 StringDBSetStringReferenced (
1679 INT8 *StringIdentifierName,
1680 BOOLEAN IgnoreNotFound
1681 )
1682 {
1683 STRING_IDENTIFIER *Id;
1684 WCHAR *WName;
1685 STATUS Status;
1686 //
1687 // See if it's already been defined.
1688 //
1689 Status = STATUS_SUCCESS;
1690 WName = (WCHAR *) malloc ((strlen (StringIdentifierName) + 1) * sizeof (WCHAR));
1691 swprintf (WName, L"%S", StringIdentifierName);
1692 Id = StringDBFindStringIdentifierByName (WName);
1693 if (Id != NULL) {
1694 Id->Flags |= STRING_FLAGS_REFERENCED;
1695 } else {
1696 if (IgnoreNotFound == 0) {
1697 ParserWarning (0, StringIdentifierName, "string identifier not found in database");
1698 Status = STATUS_WARNING;
1699 }
1700 }
1701
1702 free (WName);
1703 return Status;
1704 }
1705
1706 /*****************************************************************************/
1707
1708 /*++
1709
1710 Routine Description:
1711
1712 Dump the contents of a database to an output unicode file.
1713
1714 Arguments:
1715
1716 DBFileName - name of the pre-existing database file to read
1717 OutputFileName - name of the file to dump the database contents to
1718 Verbose - for printing of additional info useful for debugging
1719
1720 Returns:
1721
1722 STATUS
1723
1724 Notes:
1725
1726 There's some issue with the unicode printing routines. Therefore to
1727 write to the output file properly, open it as binary and use fwrite.
1728 Ideally we could open it with just L"w" and use fwprintf().
1729
1730 --*/
1731 STATUS
1732 StringDBDumpDatabase (
1733 INT8 *DBFileName,
1734 INT8 *OutputFileName,
1735 BOOLEAN Verbose
1736 )
1737 {
1738 LANGUAGE_LIST *Lang;
1739 STRING_IDENTIFIER *StringIdentifier;
1740 STRING_LIST *StrList;
1741 FILE *OutFptr;
1742 WCHAR WChar;
1743 WCHAR CrLf[2];
1744 WCHAR Line[200];
1745 WCHAR *Scope;
1746 //
1747 // This function assumes the database has already been read, and
1748 // we're just dumping our internal data structures to a unicode file.
1749 //
1750 if (Verbose) {
1751 fprintf (stdout, "Dumping database file %s\n", DBFileName);
1752 }
1753
1754 OutFptr = fopen (OutputFileName, "wb");
1755 if (OutFptr == NULL) {
1756 Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
1757 return STATUS_ERROR;
1758 }
1759
1760 WChar = UNICODE_FILE_START;
1761 fwrite (&WChar, sizeof (WCHAR), 1, OutFptr);
1762 CrLf[1] = UNICODE_LF;
1763 CrLf[0] = UNICODE_CR;
1764 //
1765 // The default control character is '/'. Make it '#' by writing
1766 // "/=#" to the output file.
1767 //
1768 swprintf (Line, L"/=#");
1769 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1770 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1771 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1772 //
1773 // Dump all the string identifiers and their values
1774 //
1775 StringDBAssignStringIndexes ();
1776 for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {
1777 //
1778 // Write the "#define " string
1779 //
1780 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
1781 swprintf (
1782 Line,
1783 L"%s %-60.60s 0x%04X",
1784 DEFINE_STR,
1785 StringIdentifier->StringName,
1786 StringIdentifier->Index
1787 );
1788 } else {
1789 swprintf (
1790 Line,
1791 L"%s %-60.60s 0x%04X // NOT REFERENCED",
1792 DEFINE_STR,
1793 StringIdentifier->StringName,
1794 StringIdentifier->Index
1795 );
1796 }
1797
1798 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1799 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1800 }
1801
1802 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1803 //
1804 // Now write all the strings for each language.
1805 //
1806 WChar = UNICODE_DOUBLE_QUOTE;
1807 Scope = NULL;
1808 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
1809 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1810 swprintf (Line, L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);
1811 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1812 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1813 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1814 //
1815 // Now the strings (in double-quotes) for this language. Write
1816 // #string STR_NAME #language eng "string"
1817 //
1818 for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {
1819 //
1820 // Print the internal flags for debug
1821 //
1822 swprintf (Line, L"// flags=0x%02X", (UINT32) StrList->Flags);
1823 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1824 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1825 //
1826 // Print the scope if changed
1827 //
1828 if ((Scope == NULL) || (wcscmp (Scope, StrList->Scope) != 0)) {
1829 swprintf (Line, L"#scope %s", StrList->Scope);
1830 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1831 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1832 Scope = StrList->Scope;
1833 }
1834
1835 swprintf (
1836 Line,
1837 L"#string %-50.50s #language %s \"",
1838 StrList->StringName,
1839 Lang->LanguageName
1840 );
1841 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1842 fwrite (StrList->Str, StrList->Size - sizeof (WCHAR), 1, OutFptr);
1843 swprintf (Line, L"\"");
1844 fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
1845 fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
1846 }
1847 }
1848
1849 fclose (OutFptr);
1850 return STATUS_SUCCESS;
1851 }
1852
1853 /*****************************************************************************/
1854
1855 /*++
1856
1857 Routine Description:
1858
1859 Given a primary language, a string identifier number, and a list of
1860 languages, find a secondary string.
1861
1862 Arguments:
1863
1864 LanguageName - primary language, like "spa"
1865 StringId - string index value
1866 LanguageList - linked list of "eng", "spa+cat",...
1867
1868 Returns:
1869
1870 Pointer to a secondary string if found. NULL otherwise.
1871
1872 Notes:
1873
1874 Given: LanguageName "spa" and LanguageList "spa+cat", match the
1875 "spa" and extract the "cat" and see if there is a string defined
1876 for "cat".StringId.
1877
1878 --*/
1879 static
1880 STATUS
1881 StringDBWriteStringIdentifier (
1882 FILE *DBFptr,
1883 UINT16 StringId,
1884 UINT16 Flags,
1885 WCHAR *IdentifierName
1886 )
1887 {
1888 DB_DATA_ITEM_HEADER Hdr;
1889 memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
1890 Hdr.DataType = DB_DATA_TYPE_STRING_IDENTIFIER;
1891 if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
1892 Error (NULL, 0, 0, "failed to write string to output database file", NULL);
1893 return STATUS_ERROR;
1894 }
1895
1896 if (fwrite (&StringId, sizeof (StringId), 1, DBFptr) != 1) {
1897 Error (NULL, 0, 0, "failed to write StringId to output database", NULL);
1898 return STATUS_ERROR;
1899 }
1900
1901 if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
1902 Error (NULL, 0, 0, "failed to write StringId flags to output database", NULL);
1903 return STATUS_ERROR;
1904 }
1905
1906 if (StringDBWriteGenericString (DBFptr, IdentifierName) != STATUS_SUCCESS) {
1907 return STATUS_ERROR;
1908 }
1909
1910 return STATUS_SUCCESS;
1911 }
1912
1913 static
1914 STATUS
1915 StringDBReadStringIdentifier (
1916 FILE *DBFptr
1917 )
1918 {
1919 WCHAR *IdentifierName;
1920 UINT16 Flags;
1921 UINT16 StringId;
1922 UINT16 Size;
1923
1924 if (fread (&StringId, sizeof (StringId), 1, DBFptr) != 1) {
1925 Error (NULL, 0, 0, "failed to read StringId from database", NULL);
1926 return STATUS_ERROR;
1927 }
1928
1929 if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
1930 Error (NULL, 0, 0, "failed to read StringId flags from database", NULL);
1931 return STATUS_ERROR;
1932 }
1933
1934 if (StringDBReadGenericString (DBFptr, &Size, &IdentifierName) != STATUS_SUCCESS) {
1935 return STATUS_ERROR;
1936 }
1937
1938 StringDBAddStringIdentifier (IdentifierName, &StringId, Flags);
1939 //
1940 // printf ("STRID: 0x%04X %S\n", (UINT32)StringId, IdentifierName);
1941 //
1942 FREE (IdentifierName);
1943 return STATUS_SUCCESS;
1944 }
1945
1946 static
1947 STATUS
1948 StringDBWriteString (
1949 FILE *DBFptr,
1950 UINT16 Flags,
1951 WCHAR *Language,
1952 WCHAR *StringName,
1953 WCHAR *Scope,
1954 WCHAR *Str
1955 )
1956 {
1957 DB_DATA_ITEM_HEADER Hdr;
1958 memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
1959 Hdr.DataType = DB_DATA_TYPE_STRING_DEFINITION;
1960 if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
1961 Error (NULL, 0, 0, "failed to write string header to output database file", NULL);
1962 return STATUS_ERROR;
1963 }
1964
1965 if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
1966 Error (NULL, 0, 0, "failed to write string flags to output database", NULL);
1967 return STATUS_ERROR;
1968 }
1969
1970 if (StringDBWriteGenericString (DBFptr, Language) != STATUS_SUCCESS) {
1971 return STATUS_ERROR;
1972 }
1973
1974 if (StringDBWriteGenericString (DBFptr, StringName) != STATUS_SUCCESS) {
1975 return STATUS_ERROR;
1976 }
1977
1978 if (StringDBWriteGenericString (DBFptr, Scope) != STATUS_SUCCESS) {
1979 return STATUS_ERROR;
1980 }
1981
1982 if (StringDBWriteGenericString (DBFptr, Str) != STATUS_SUCCESS) {
1983 return STATUS_ERROR;
1984 }
1985 //
1986 // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);
1987 //
1988 return STATUS_SUCCESS;
1989 }
1990
1991 static
1992 STATUS
1993 StringDBReadString (
1994 FILE *DBFptr
1995 )
1996 {
1997 UINT16 Flags;
1998 UINT16 Size;
1999 WCHAR *Language;
2000 WCHAR *StringName;
2001 WCHAR *Scope;
2002 WCHAR *Str;
2003
2004 if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
2005 Error (NULL, 0, 0, "failed to read string flags from database", NULL);
2006 return STATUS_ERROR;
2007 }
2008
2009 if (StringDBReadGenericString (DBFptr, &Size, &Language) != STATUS_SUCCESS) {
2010 return STATUS_ERROR;
2011 }
2012
2013 if (StringDBReadGenericString (DBFptr, &Size, &StringName) != STATUS_SUCCESS) {
2014 return STATUS_ERROR;
2015 }
2016
2017 if (StringDBReadGenericString (DBFptr, &Size, &Scope) != STATUS_SUCCESS) {
2018 return STATUS_ERROR;
2019 }
2020
2021 if (StringDBReadGenericString (DBFptr, &Size, &Str) != STATUS_SUCCESS) {
2022 return STATUS_ERROR;
2023 }
2024 //
2025 // If the first or second string (language name and printable language name),
2026 // then skip them. They're added via language definitions data items in
2027 // the database.
2028 //
2029 if (StringName[0] != L'$') {
2030 StringDBAddString (Language, StringName, Scope, Str, FALSE, Flags);
2031 }
2032 //
2033 // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);
2034 //
2035 FREE (Language);
2036 FREE (StringName);
2037 if (Str != NULL) {
2038 FREE (Str);
2039 }
2040
2041 if (Scope != NULL) {
2042 FREE (Scope);
2043 }
2044
2045 return STATUS_SUCCESS;
2046 }
2047
2048 static
2049 STATUS
2050 StringDBWriteLanguageDefinition (
2051 FILE *DBFptr,
2052 WCHAR *LanguageName,
2053 WCHAR *PrintableLanguageName
2054 )
2055 {
2056 DB_DATA_ITEM_HEADER Hdr;
2057 memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
2058 Hdr.DataType = DB_DATA_TYPE_LANGUAGE_DEFINITION;
2059 if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
2060 Error (NULL, 0, 0, "failed to write string to output database file", NULL);
2061 return STATUS_ERROR;
2062 }
2063
2064 if (StringDBWriteGenericString (DBFptr, LanguageName) != STATUS_SUCCESS) {
2065 return STATUS_ERROR;
2066 }
2067
2068 if (StringDBWriteGenericString (DBFptr, PrintableLanguageName) != STATUS_SUCCESS) {
2069 return STATUS_ERROR;
2070 }
2071
2072 return STATUS_SUCCESS;
2073 }
2074
2075 static
2076 STATUS
2077 StringDBReadLanguageDefinition (
2078 FILE *DBFptr
2079 )
2080 {
2081 WCHAR *LanguageName;
2082 WCHAR *PrintableLanguageName;
2083 UINT16 Size;
2084 STATUS Status;
2085
2086 if (StringDBReadGenericString (DBFptr, &Size, &LanguageName) != STATUS_SUCCESS) {
2087 return STATUS_ERROR;
2088 }
2089
2090 if (StringDBReadGenericString (DBFptr, &Size, &PrintableLanguageName) != STATUS_SUCCESS) {
2091 return STATUS_ERROR;
2092 }
2093 //
2094 // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);
2095 //
2096 Status = StringDBAddLanguage (LanguageName, PrintableLanguageName);
2097 FREE (LanguageName);
2098 FREE (PrintableLanguageName);
2099 return Status;
2100 }
2101 //
2102 // All unicode strings in the database consist of a UINT16 length
2103 // field, followed by the string itself. This routine reads one
2104 // of those and returns the info.
2105 //
2106 static
2107 STATUS
2108 StringDBReadGenericString (
2109 FILE *DBFptr,
2110 UINT16 *Size,
2111 WCHAR **Str
2112 )
2113 {
2114 UINT16 LSize;
2115 UINT16 Flags;
2116 WCHAR *LStr;
2117
2118 if (fread (&LSize, sizeof (UINT16), 1, DBFptr) != 1) {
2119 Error (NULL, 0, 0, "failed to read a string length field from the database", NULL);
2120 return STATUS_ERROR;
2121 }
2122
2123 if (fread (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {
2124 Error (NULL, 0, 0, "failed to read a string flags field from the database", NULL);
2125 return STATUS_ERROR;
2126 }
2127
2128 LStr = MALLOC (LSize);
2129 if (LStr == NULL) {
2130 Error (__FILE__, __LINE__, 0, "memory allocation failed reading the database", NULL);
2131 return STATUS_ERROR;
2132 }
2133
2134 if (fread (LStr, sizeof (WCHAR), (UINT32) LSize / sizeof (WCHAR), DBFptr) != (UINT32) LSize / sizeof (WCHAR)) {
2135 Error (NULL, 0, 0, "failed to read string from database", NULL);
2136 Error (NULL, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr));
2137 free (LStr);
2138 return STATUS_ERROR;
2139 }
2140 //
2141 // printf ("DBR: %S\n", LStr);
2142 //
2143 // If the flags field indicated we were asked to write a NULL string, then
2144 // return them a NULL pointer.
2145 //
2146 if (Flags & STRING_FLAGS_UNDEFINED) {
2147 *Size = 0;
2148 *Str = NULL;
2149 } else {
2150 *Size = LSize;
2151 *Str = LStr;
2152 }
2153
2154 return STATUS_SUCCESS;
2155 }
2156
2157 static
2158 STATUS
2159 StringDBWriteGenericString (
2160 FILE *DBFptr,
2161 WCHAR *Str
2162 )
2163 {
2164 UINT16 Size;
2165 UINT16 Flags;
2166 WCHAR ZeroString[1];
2167 //
2168 // Strings in the database consist of a size UINT16 followed
2169 // by the string itself.
2170 //
2171 if (Str == NULL) {
2172 ZeroString[0] = 0;
2173 Str = ZeroString;
2174 Size = sizeof (ZeroString);
2175 Flags = STRING_FLAGS_UNDEFINED;
2176 } else {
2177 Flags = 0;
2178 Size = (UINT16) ((wcslen (Str) + 1) * sizeof (WCHAR));
2179 }
2180
2181 if (fwrite (&Size, sizeof (UINT16), 1, DBFptr) != 1) {
2182 Error (NULL, 0, 0, "failed to write string size to database", NULL);
2183 return STATUS_ERROR;
2184 }
2185
2186 if (fwrite (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {
2187 Error (NULL, 0, 0, "failed to write string flags to database", NULL);
2188 return STATUS_ERROR;
2189 }
2190
2191 if (fwrite (Str, sizeof (WCHAR), Size / sizeof (WCHAR), DBFptr) != Size / sizeof (WCHAR)) {
2192 Error (NULL, 0, 0, "failed to write string to database", NULL);
2193 return STATUS_ERROR;
2194 }
2195
2196 return STATUS_SUCCESS;
2197 }
2198
2199 static
2200 STRING_LIST *
2201 StringDBFindString (
2202 WCHAR *LanguageName,
2203 WCHAR *StringName,
2204 WCHAR *Scope,
2205 WCHAR_STRING_LIST *LanguagesOfInterest,
2206 WCHAR_MATCHING_STRING_LIST *IndirectionList
2207 )
2208 {
2209 LANGUAGE_LIST *Lang;
2210 STRING_LIST *CurrString;
2211 WCHAR_MATCHING_STRING_LIST *IndListPtr;
2212 WCHAR TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN + 1];
2213 WCHAR *WCharPtr;
2214
2215 //
2216 // If we were given an indirection list, then see if one was specified for this
2217 // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",
2218 // then if this string name matches one in the list, then do a lookup with the
2219 // specified scope and return that value.
2220 //
2221 if (IndirectionList != NULL) {
2222 for (IndListPtr = IndirectionList; IndListPtr != NULL; IndListPtr = IndListPtr->Next) {
2223 if (wcscmp (StringName, IndListPtr->Str1) == 0) {
2224 CurrString = StringDBFindString (LanguageName, StringName, IndListPtr->Str2, LanguagesOfInterest, NULL);
2225 if (CurrString != NULL) {
2226 return CurrString;
2227 }
2228 }
2229 }
2230 }
2231 //
2232 // First look for exact match language.stringname
2233 //
2234 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
2235 if (wcscmp (LanguageName, Lang->LanguageName) == 0) {
2236 //
2237 // Found language match. Try to find string name match
2238 //
2239 for (CurrString = Lang->String; CurrString != NULL; CurrString = CurrString->Next) {
2240 if (wcscmp (StringName, CurrString->StringName) == 0) {
2241 //
2242 // Found a string name match. See if we're supposed to find
2243 // a scope match.
2244 //
2245 if (Scope != NULL) {
2246 if (wcscmp (CurrString->Scope, Scope) == 0) {
2247 return CurrString;
2248 }
2249 } else {
2250 return CurrString;
2251 }
2252 }
2253 }
2254 }
2255 }
2256 //
2257 // If we got here, then we didn't find a match. Look for secondary string
2258 // matches. That is to say, if we're processing "spa", and they requested
2259 // "spa+cat", then recursively call with "cat"
2260 //
2261 while (LanguagesOfInterest != NULL) {
2262 //
2263 // If this is the language we're looking for, then process the
2264 // languages of interest list for it.
2265 //
2266 if (wcsncmp (LanguageName, LanguagesOfInterest->Str, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
2267 WCharPtr = LanguagesOfInterest->Str + LANGUAGE_IDENTIFIER_NAME_LEN;
2268 while (*WCharPtr) {
2269 //
2270 // Double-check the length, though it should have been checked on the
2271 // command line.
2272 //
2273 if (wcslen (WCharPtr) < LANGUAGE_IDENTIFIER_NAME_LEN) {
2274 Error (NULL, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest->Str);
2275 return NULL;
2276 }
2277
2278 wcsncpy (TempLangName, WCharPtr, LANGUAGE_IDENTIFIER_NAME_LEN);
2279 TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN] = 0;
2280 CurrString = StringDBFindString (TempLangName, StringName, NULL, NULL, IndirectionList);
2281 if (CurrString != NULL) {
2282 return CurrString;
2283 }
2284
2285 WCharPtr += LANGUAGE_IDENTIFIER_NAME_LEN;
2286 }
2287 }
2288
2289 LanguagesOfInterest = LanguagesOfInterest->Next;
2290 }
2291
2292 return NULL;
2293 }
2294
2295 STATUS
2296 StringDBSetScope (
2297 WCHAR *Scope
2298 )
2299 {
2300 //
2301 // Free up existing scope memory.
2302 //
2303 if (mDBData.CurrentScope != NULL) {
2304 FREE (mDBData.CurrentScope);
2305 }
2306
2307 mDBData.CurrentScope = DuplicateString (Scope);
2308 return STATUS_SUCCESS;
2309 }
2310 //
2311 // We typically don't assign index values to string identifiers
2312 // until we're ready to write out files. To reduce the size of
2313 // the output file, re-order the string identifiers to move any
2314 // unreferenced ones to the end. Then we'll walk the list
2315 // again to assign string indexes, keeping track of the last
2316 // one referenced.
2317 //
2318 static
2319 void
2320 StringDBAssignStringIndexes (
2321 VOID
2322 )
2323 {
2324 STRING_IDENTIFIER *StrId;
2325 STRING_IDENTIFIER *FirstUsed;
2326 STRING_IDENTIFIER *LastUsed;
2327 STRING_IDENTIFIER *FirstUnused;
2328 STRING_IDENTIFIER *LastUnused;
2329 UINT32 Index;
2330 UINT32 MaxReferenced;
2331
2332 //
2333 // Create two lists -- used and unused. Then put them together with
2334 // the unused ones on the end.
2335 //
2336 FirstUsed = NULL;
2337 LastUsed = NULL;
2338 FirstUnused = NULL;
2339 LastUnused = NULL;
2340 StrId = mDBData.StringIdentifier;
2341 while (StrId != NULL) {
2342 if ((StrId->Flags & STRING_FLAGS_REFERENCED) == 0) {
2343 //
2344 // Put it on the unused list
2345 //
2346 if (FirstUnused == NULL) {
2347 FirstUnused = StrId;
2348 } else {
2349 LastUnused->Next = StrId;
2350 }
2351
2352 LastUnused = StrId;
2353 StrId = StrId->Next;
2354 LastUnused->Next = NULL;
2355 } else {
2356 //
2357 // Put it on the used list
2358 //
2359 if (FirstUsed == NULL) {
2360 FirstUsed = StrId;
2361 } else {
2362 LastUsed->Next = StrId;
2363 }
2364
2365 LastUsed = StrId;
2366 StrId = StrId->Next;
2367 LastUsed->Next = NULL;
2368 }
2369 }
2370 //
2371 // Join the lists
2372 //
2373 if (FirstUsed != NULL) {
2374 mDBData.StringIdentifier = FirstUsed;
2375 LastUsed->Next = FirstUnused;
2376 } else {
2377 mDBData.StringIdentifier = FirstUnused;
2378 }
2379
2380 MaxReferenced = 0;
2381 Index = 0;
2382 for (StrId = mDBData.StringIdentifier; StrId != NULL; StrId = StrId->Next) {
2383 StrId->Index = Index;
2384 Index++;
2385 if (StrId->Flags & STRING_FLAGS_REFERENCED) {
2386 mDBData.NumStringIdentifiersReferenced = Index;
2387 }
2388 }
2389
2390 mDBData.NumStringIdentifiers = Index;
2391 }
2392
2393 static
2394 WCHAR *
2395 DuplicateString (
2396 WCHAR *Str
2397 )
2398 {
2399 WCHAR *NewStr;
2400 if (Str == NULL) {
2401 return NULL;
2402 }
2403
2404 NewStr = MALLOC ((wcslen (Str) + 1) * sizeof (WCHAR));
2405 if (NewStr == NULL) {
2406 Error (NULL, 0, 0, "memory allocation failure", NULL);
2407 return NULL;
2408 }
2409
2410 wcscpy (NewStr, Str);
2411 return NewStr;
2412 }
2413
2414 static
2415 WCHAR *
2416 AsciiToWchar (
2417 INT8 *Str
2418 )
2419 {
2420 UINT32 Len;
2421 WCHAR *NewStr;
2422 WCHAR *Ptr;
2423
2424 Len = strlen (Str) + 1;
2425 NewStr = (WCHAR *) malloc (Len * sizeof (WCHAR));
2426 for (Ptr = NewStr; *Str != 0; Str++, Ptr++) {
2427 *Ptr = (UINT16) (UINT8) *Str;
2428 }
2429
2430 *Ptr = 0;
2431 return NewStr;
2432 }
2433
2434 /*****************************************************************************/
2435
2436 /*++
2437
2438 Routine Description:
2439
2440 Create an HII export string pack for the strings in our database.
2441
2442 Arguments:
2443
2444 FileName - name of the output file to write
2445
2446 Returns:
2447
2448 STATUS
2449
2450
2451 --*/
2452 STATUS
2453 StringDBCreateHiiExportPack (
2454 INT8 *FileName
2455 )
2456 {
2457 FILE *Fptr;
2458 LANGUAGE_LIST *Lang;
2459 STRING_LIST *CurrString;
2460 STRING_LIST EmptyString;
2461 UINT32 Offset;
2462 UINT32 StringIndex;
2463 UINT32 TempIndex;
2464 EFI_HII_STRING_PACK StringPack;
2465 UINT32 Len;
2466 WCHAR ZeroString[1];
2467 WCHAR *TempStringPtr;
2468 WCHAR *LangName;
2469 STRING_IDENTIFIER *StringIdentifier;
2470
2471 if ((Fptr = fopen (FileName, "wb")) == NULL) {
2472 Error (NULL, 0, 0, FileName, "failed to open output HII export file");
2473 return STATUS_ERROR;
2474 }
2475 //
2476 // Assign index values to the string identifiers
2477 //
2478 StringDBAssignStringIndexes ();
2479 //
2480 // If a given string is not defined, then we'll use this one.
2481 //
2482 memset (&EmptyString, 0, sizeof (EmptyString));
2483 EmptyString.Size = sizeof (ZeroString);
2484 EmptyString.Str = ZeroString;
2485 //
2486 // Process each language, then each string for each langage
2487 //
2488 ZeroString[0] = 0;
2489 for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
2490 //
2491 // Process each string for this language. We have to make 3 passes on the strings:
2492 // Pass1: computes sizes and fill in the string pack header
2493 // Pass2: write the array of offsets
2494 // Pass3: write the strings
2495 //
2496 //
2497 // PASS 1: Fill in and print the HII string pack header
2498 //
2499 // Compute the size for this language package and write
2500 // the header out. Each string package contains:
2501 // Header
2502 // Offset[] -- an array of offsets to strings, of type RELOFST each
2503 // String[] -- the actual strings themselves
2504 //
2505 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK));
2506 StringPack.Header.Type = EFI_HII_STRING;
2507 StringPack.NumStringPointers = (UINT16) mDBData.NumStringIdentifiersReferenced;
2508 LangName = Lang->LanguageName;
2509 //
2510 // First string is the language name. If we're printing all languages, then
2511 // it's just the "spa". If we were given a list of languages to print, then it's
2512 // the "spacat" string. Compute its offset and fill in
2513 // the info in the header. Since we know the language name string's length,
2514 // and the printable language name follows it, use that info to fill in the
2515 // entry for the printable language name as well.
2516 //
2517 StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));
2518 StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));
2519 //
2520 // Add up the size of all strings so we can fill in our header.
2521 //
2522 Len = 0;
2523 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
2524 //
2525 // For the first string (language name), we print out the "spacat" if they
2526 // requested it. We set LangName to point to the proper language name string above.
2527 //
2528 if (StringIndex == STRING_ID_LANGUAGE_NAME) {
2529 Len += (wcslen (LangName) + 1) * sizeof (WCHAR);
2530 } else {
2531 //
2532 // Find a string with this language.stringname
2533 //
2534 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
2535 if (StringIdentifier == NULL) {
2536 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
2537 return STATUS_ERROR;
2538 }
2539 //
2540 // Find a matching string if this string identifier was referenced
2541 //
2542 EmptyString.Flags = STRING_FLAGS_UNDEFINED;
2543 CurrString = NULL;
2544 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
2545 CurrString = StringDBFindString (
2546 Lang->LanguageName,
2547 StringIdentifier->StringName,
2548 NULL,
2549 NULL, // LanguagesOfInterest,
2550 NULL
2551 );
2552 //
2553 // IndirectionList);
2554 //
2555 if (NULL == CurrString) {
2556 //
2557 // If string for Lang->LanguageName is not found, try to get an English version
2558 //
2559 CurrString = StringDBFindString (
2560 L"eng",
2561 StringIdentifier->StringName,
2562 NULL,
2563 NULL, // LanguagesOfInterest,
2564 NULL
2565 );
2566 //
2567 // IndirectionList);
2568 //
2569 }
2570 }
2571
2572 if (CurrString == NULL) {
2573 CurrString = &EmptyString;
2574 EmptyString.Flags |= StringIdentifier->Flags;
2575 }
2576
2577 Len += CurrString->Size;
2578 }
2579 }
2580 StringPack.Header.Length = sizeof (EFI_HII_STRING_PACK)
2581 + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)
2582 + Len;
2583 //
2584 // Write out the string pack header
2585 //
2586 fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);
2587 //
2588 // PASS2 : write the offsets
2589 //
2590 // Traverse the list of strings again and write the array of offsets. The
2591 // offset to the first string is the size of the string pack header
2592 // plus the size of the offsets array. The other strings follow it.
2593 //
2594 StringIndex = 0;
2595 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
2596 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
2597 //
2598 // Write the offset
2599 //
2600 fwrite (&Offset, sizeof (STRING_OFFSET), 1, Fptr);
2601 //
2602 // Find the string name
2603 //
2604 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
2605 if (StringIdentifier == NULL) {
2606 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
2607 return STATUS_ERROR;
2608 }
2609 //
2610 // For the first string (language name), we print out the "spacat" if they
2611 // requested it. We set LangName to point to the proper language name string above.
2612 //
2613 if (StringIndex == STRING_ID_LANGUAGE_NAME) {
2614 Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);
2615 CurrString = StringDBFindString (
2616 Lang->LanguageName,
2617 StringIdentifier->StringName,
2618 NULL, // scope
2619 NULL,
2620 NULL
2621 );
2622 } else {
2623 //
2624 // Find a matching string
2625 //
2626 CurrString = StringDBFindString (
2627 Lang->LanguageName,
2628 StringIdentifier->StringName,
2629 NULL, // scope
2630 NULL, // LanguagesOfInterest,
2631 NULL
2632 );
2633 //
2634 // IndirectionList);
2635 //
2636 if (NULL == CurrString) {
2637 CurrString = StringDBFindString (
2638 L"eng",
2639 StringIdentifier->StringName,
2640 NULL, // scope
2641 NULL, // LanguagesOfInterest,
2642 NULL
2643 );
2644 //
2645 // IndirectionList);
2646 //
2647 }
2648
2649 EmptyString.LanguageName = Lang->LanguageName;
2650 if (CurrString == NULL) {
2651 CurrString = &EmptyString;
2652 EmptyString.Flags = STRING_FLAGS_UNDEFINED;
2653 } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
2654 CurrString = &EmptyString;
2655 EmptyString.Flags = 0;
2656 }
2657
2658 Offset += CurrString->Size;
2659 }
2660 }
2661
2662 //
2663 // PASS 3: write the strings themselves.
2664 //
2665 Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
2666 for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
2667 StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
2668 if (StringIdentifier == NULL) {
2669 Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
2670 return STATUS_ERROR;
2671 }
2672 //
2673 // For the first string (language name), we print out the "spacat" if they
2674 // requested it. We set LangName to point to the proper language name string above.
2675 //
2676 if (StringIndex == STRING_ID_LANGUAGE_NAME) {
2677 TempStringPtr = LangName;
2678 } else {
2679 //
2680 // Find a matching string if this string identifier was referenced
2681 //
2682 CurrString = NULL;
2683 if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
2684 CurrString = StringDBFindString (
2685 Lang->LanguageName,
2686 StringIdentifier->StringName,
2687 NULL, // scope
2688 NULL, // LanguagesOfInterest,
2689 NULL
2690 );
2691 //
2692 // IndirectionList);
2693 //
2694 if (NULL == CurrString) {
2695 CurrString = StringDBFindString (
2696 L"eng",
2697 StringIdentifier->StringName,
2698 NULL, // scope
2699 NULL, // LanguagesOfInterest,
2700 NULL
2701 );
2702 //
2703 // IndirectionList);
2704 //
2705 }
2706 }
2707
2708 if (CurrString == NULL) {
2709 CurrString = &EmptyString;
2710 }
2711
2712 TempStringPtr = CurrString->Str;
2713 }
2714
2715 for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {
2716 fwrite (&TempStringPtr[TempIndex], sizeof (CHAR16), 1, Fptr);
2717 Offset += 2;
2718 }
2719 //
2720 // Print NULL WCHAR at the end of this string.
2721 //
2722 TempIndex = 0;
2723 fwrite (&TempIndex, sizeof (CHAR16), 1, Fptr);
2724 Offset += 2;
2725 }
2726 //
2727 // Sanity check the offset. Make sure our running offset is what we put in the
2728 // string pack header.
2729 //
2730 if (StringPack.Header.Length != Offset) {
2731 Error (
2732 __FILE__,
2733 __LINE__,
2734 0,
2735 "application error",
2736 "stringpack size 0x%X does not match final size 0x%X",
2737 StringPack.Header.Length,
2738 Offset
2739 );
2740 }
2741 }
2742 //
2743 // Print terminator string pack, closing brace and close the file.
2744 // The size of 0 triggers to the consumer that this is the end.
2745 //
2746 memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK));
2747 StringPack.Header.Type = EFI_HII_STRING;
2748 fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);
2749 fclose (Fptr);
2750 return STATUS_SUCCESS;
2751 }