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