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