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