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