home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 March / CMCD0304.ISO / Software / Freeware / Programare / nullsoft / nsis20.exe / Source / lang.cpp < prev    next >
C/C++ Source or Header  |  2004-02-05  |  31KB  |  949 lines

  1. #include "Platform.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include "build.h"
  5. #include "DialogTemplate.h"
  6. #include "exehead\resource.h"
  7.  
  8. extern const char *NSIS_VERSION;
  9.  
  10. // Default English strings. Should match NSIS_DEFAULT_LANG
  11. // Do not change the first string in every item, it's the LangString
  12. // name for usage in scripts.
  13.  
  14. typedef enum {
  15.   NONE_STATIC = 0,
  16.   INSTALL_STATIC = 1,
  17.   UNINSTALL_STATIC = 2,
  18.   BOTH_STATIC = 3
  19. } STATICID;
  20.  
  21. struct {
  22.   char *szLangStringName;
  23.   char *szDefault;
  24.   STATICID eStaticID;
  25. } NLFStrings[NLF_STRINGS] = {
  26.   {"^Branding", "Nullsoft Install System %s", BOTH_STATIC},
  27.   {"^SetupCaption", "$(^Name) Setup", INSTALL_STATIC},
  28.   {"^UninstallCaption", "$(^Name) Uninstall", UNINSTALL_STATIC},
  29.   {"^LicenseSubCaption", ": License Agreement", NONE_STATIC},
  30.   {"^ComponentsSubCaption", ": Installation Options", NONE_STATIC},
  31.   {"^DirSubCaption", ": Installation Folder", NONE_STATIC},
  32.   {"^InstallingSubCaption", ": Installing", NONE_STATIC},
  33.   {"^CompletedSubCaption", ": Completed", NONE_STATIC},
  34.   {"^UnComponentsSubCaption", ": Uninstallation Options", NONE_STATIC},
  35.   {"^UnDirSubCaption", ": Uninstallation Folder", NONE_STATIC},
  36.   {"^ConfirmSubCaption", ": Confirmation", NONE_STATIC},
  37.   {"^UninstallingSubCaption", ": Uninstalling", NONE_STATIC},
  38.   {"^UnCompletedSubCaption", ": Completed", NONE_STATIC},
  39.   {"^BackBtn", "< &Back", NONE_STATIC},
  40.   {"^NextBtn", "&Next >", NONE_STATIC},
  41.   {"^AgreeBtn", "I &Agree", NONE_STATIC},
  42.   {"^AcceptBtn", "I &accept the terms in the License Agreement", NONE_STATIC},
  43.   {"^DontAcceptBtn", "I &do not accept the terms in the License Agreement", NONE_STATIC},
  44.   {"^InstallBtn", "&Install", NONE_STATIC},
  45.   {"^UninstallBtn", "&Uninstall", NONE_STATIC},
  46.   {"^CancelBtn", "Cancel", NONE_STATIC},
  47.   {"^CloseBtn", "&Close", NONE_STATIC},
  48.   {"^BrowseBtn", "B&rowse...", NONE_STATIC},
  49.   {"^ShowDetailsBtn", "Show &details", NONE_STATIC},
  50.   {"^ClickNext", "Click Next to continue.", NONE_STATIC},
  51.   {"^ClickInstall", "Click Install to start the installation.", NONE_STATIC},
  52.   {"^ClickUninstall", "Click Uninstall to start the uninstallation.", NONE_STATIC},
  53.   {"^Name", "Name", BOTH_STATIC},
  54.   {"^NameDA", 0, NONE_STATIC}, // virtual
  55.   {"^Completed", "Completed", NONE_STATIC},
  56.   {"^LicenseText", "Please review the license agreement before installing $(^NameDA). If you accept all terms of the agreement, click I Agree.", NONE_STATIC},
  57.   {"^LicenseTextCB", "Please review the license agreement before installing $(^NameDA). If you accept all terms of the agreement, click the check box below. $_CLICK", NONE_STATIC},
  58.   {"^LicenseTextRB", "Please review the license agreement before installing $(^NameDA). If you accept all terms of the agreement, select the first option below. $_CLICK", NONE_STATIC},
  59.   {"^UnLicenseText", "Please review the license agreement before uninstalling $(^NameDA). If you accept all terms of the agreement, click I Agree.", NONE_STATIC},
  60.   {"^UnLicenseTextCB", "Please review the license agreement before uninstalling $(^NameDA). If you accept all terms of the agreement, click the check box below. $_CLICK", NONE_STATIC},
  61.   {"^UnLicenseTextRB", "Please review the license agreement before uninstalling $(^NameDA). If you accept all terms of the agreement, select the first option below. $_CLICK", NONE_STATIC},
  62.   {"^LicenseData", 0, NONE_STATIC}, // virtual - not processed
  63.   {"^Custom", "Custom", NONE_STATIC},
  64.   {"^ComponentsText", "Check the components you want to install and uncheck the components you don't want to install. $_CLICK", NONE_STATIC},
  65.   {"^ComponentsSubText1", "Select the type of install:", NONE_STATIC},
  66.   {"^ComponentsSubText2_NoInstTypes", "Select components to install:", NONE_STATIC},
  67.   {"^ComponentsSubText2", "Or, select the optional components you wish to install:", NONE_STATIC},
  68.   {"^UnComponentsText", "Check the components you want to uninstall and uncheck the components you don't want to uninstall. $_CLICK", NONE_STATIC},
  69.   {"^UnComponentsSubText1", "Select the type of uninstall:", NONE_STATIC},
  70.   {"^UnComponentsSubText2_NoInstTypes", "Select components to uninstall:", NONE_STATIC},
  71.   {"^UnComponentsSubText2", "Or, select the optional components you wish to uninstall:", NONE_STATIC},
  72.   {"^DirText", "Setup will install $(^NameDA) in the following folder. To install in a different folder, click Browse and select another folder. $_CLICK", NONE_STATIC},
  73.   {"^DirSubText", "Destination Folder", NONE_STATIC},
  74.   {"^DirBrowseText", "Select the folder to install $(^NameDA) in:", NONE_STATIC},
  75.   {"^UnDirText", "Setup will uninstall $(^NameDA) from the following folder. To uninstall from a different folder, click Browse and select another folder. $_CLICK", NONE_STATIC},
  76.   {"^UnDirSubText", "", NONE_STATIC},
  77.   {"^UnDirBrowseText", "Select the folder to uninstall $(^NameDA) from:", NONE_STATIC},
  78.   {"^SpaceAvailable", "Space available: ", BOTH_STATIC},
  79.   {"^SpaceRequired", "Space required: ", BOTH_STATIC},
  80.   {"^UninstallingText", "This wizard will uninstall $(^NameDA) from your computer. $_CLICK", NONE_STATIC},
  81.   {"^UninstallingSubText", "Uninstalling from:", NONE_STATIC},
  82.   {"^FileError", "Error opening file for writing: \r\n\t\"$0\"\r\nHit abort to abort installation,\r\nretry to retry writing the file, or\r\nignore to skip this file", NONE_STATIC},
  83.   {"^FileError_NoIgnore", "Error opening file for writing: \r\n\t\"$0\"\r\nHit retry to retry writing the file, or\r\ncancel to abort installation", NONE_STATIC},
  84.   {"^CantWrite", "Can't write: ", BOTH_STATIC},
  85.   {"^CopyFailed", "Copy failed", BOTH_STATIC},
  86.   {"^CopyTo", "Copy to ", BOTH_STATIC},
  87.   {"^Registering", "Registering: ", NONE_STATIC},
  88.   {"^Unregistering", "Unregistering: ", NONE_STATIC},
  89.   {"^SymbolNotFound", "Could not find symbol: ", BOTH_STATIC},
  90.   {"^CouldNotLoad", "Could not load: ", BOTH_STATIC},
  91.   {"^CreateFolder", "Create folder: ", BOTH_STATIC},
  92.   {"^CreateShortcut", "Create shortcut: ", BOTH_STATIC},
  93.   {"^CreatedUninstaller", "Created uninstaller: ", BOTH_STATIC},
  94.   {"^Delete", "Delete file: ", BOTH_STATIC},
  95.   {"^DeleteOnReboot", "Delete on reboot: ", BOTH_STATIC},
  96.   {"^ErrorCreatingShortcut", "Error creating shortcut: ", BOTH_STATIC},
  97.   {"^ErrorCreating", "Error creating: ", BOTH_STATIC},
  98.   {"^ErrorDecompressing", "Error decompressing data! Corrupted installer?", BOTH_STATIC},
  99.   {"^ErrorRegistering", "Error registering DLL", BOTH_STATIC},
  100.   {"^ExecShell", "ExecShell: ", BOTH_STATIC},
  101.   {"^Exec", "Execute: ", BOTH_STATIC},
  102.   {"^Extract", "Extract: ", BOTH_STATIC},
  103.   {"^ErrorWriting", "Extract: error writing to file ", BOTH_STATIC},
  104.   {"^InvalidOpcode", "Installer corrupted: invalid opcode", BOTH_STATIC},
  105.   {"^NoOLE", "No OLE for: ", BOTH_STATIC},
  106.   {"^OutputFolder", "Output folder: ", BOTH_STATIC},
  107.   {"^RemoveFolder", "Remove folder: ", BOTH_STATIC},
  108.   {"^RenameOnReboot", "Rename on reboot: ", BOTH_STATIC},
  109.   {"^Rename", "Rename: ", BOTH_STATIC},
  110.   {"^Skipped", "Skipped: ", BOTH_STATIC},
  111.   {"^CopyDetails", "Copy Details To Clipboard", BOTH_STATIC},
  112.   {"^LogInstall", "Log install process", BOTH_STATIC},
  113.   {"^Byte", "B", BOTH_STATIC},
  114.   {"^Kilo", "K", BOTH_STATIC},
  115.   {"^Mega", "M", BOTH_STATIC},
  116.   {"^Giga", "G", BOTH_STATIC},
  117.   {"^Font", "MS Shell Dlg", NONE_STATIC},
  118.   {"^FontSize", "8", NONE_STATIC},
  119.   {"^RTL", "0", NONE_STATIC}
  120. };
  121.  
  122. void CEXEBuild::InitLangTables() {
  123.   keep_ref = false;
  124.  
  125.   for (int i = 0; i < NLF_STRINGS; i++) {
  126.     NLFRefs[i].iRef = 0;
  127.     NLFRefs[i].iUnRef = 0;
  128.  
  129. #ifdef NSIS_CONFIG_LOG
  130.     if (i == NLF_NAME) {
  131.       NLFRefs[i].iRef++;
  132.       NLFRefs[i].iUnRef++;
  133.     }
  134. #endif
  135.  
  136.     if (NLFStrings[i].eStaticID & INSTALL_STATIC) {
  137.       set_uninstall_mode(0);
  138.       DefineLangString(NLFStrings[i].szLangStringName);
  139.     }
  140.  
  141. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  142.     if (NLFStrings[i].eStaticID & UNINSTALL_STATIC) {
  143.       set_uninstall_mode(1);
  144.       DefineLangString(NLFStrings[i].szLangStringName);
  145.     }
  146. #endif
  147.   }
  148.  
  149.   set_uninstall_mode(0);
  150.  
  151.   keep_ref = true;
  152. }
  153.  
  154. LanguageTable* CEXEBuild::GetLangTable(LANGID &lang) {
  155.   int nlt = lang_tables.getlen() / sizeof(LanguageTable);
  156.   LanguageTable *nla = (LanguageTable*)lang_tables.get();
  157.  
  158.   lang = lang ? lang : last_used_lang;
  159.   last_used_lang = lang;
  160.   LanguageTable *table = 0;
  161.  
  162.   for (int i = 0; i < nlt; i++) {
  163.     if (lang == nla[i].lang_id) {
  164.       table = &nla[i];
  165.       break;
  166.     }
  167.   }
  168.   if (!table) {
  169.     LanguageTable newtable;
  170.  
  171.     newtable.lang_id = lang;
  172.     newtable.dlg_offset = 0;
  173.     memset(&newtable.nlf, 0, sizeof(NLF));
  174.  
  175.     newtable.lang_strings = new StringsArray;
  176.  
  177.     lang_tables.add(&newtable, sizeof(LanguageTable));
  178.     table = (LanguageTable*)lang_tables.get() + nlt;
  179.   }
  180.  
  181.   return table;
  182. }
  183.  
  184. int CEXEBuild::DefineLangString(char *name, int process/*=-1*/) {
  185.   int index, uindex, pos, ret, sn;
  186.   pos = build_langstrings.get(name, &sn, &index, &uindex);
  187.   if (pos < 0) {
  188.     pos = build_langstrings.add(name);
  189.   }
  190.  
  191. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  192.   if (!uninstall_mode) {
  193. #endif
  194.     if (index < 0) {
  195.       index = build_langstring_num++;
  196.     }
  197.     ret = -index - 1;
  198. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  199.   }
  200.   else {
  201.     if (uindex < 0) {
  202.       uindex = ubuild_langstring_num++;
  203.     }
  204.     ret = -uindex - 1;
  205.   }
  206. #endif
  207.  
  208.   build_langstrings.set(pos, index, uindex, process);
  209.  
  210.   // set reference count for NLF strings
  211.   if (keep_ref && name[0] == '^') {
  212.     for (int i = 0; i < NLF_STRINGS; i++) {
  213.       if (!strcmp(name, NLFStrings[i].szLangStringName)) {
  214. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  215.         if (uninstall_mode)
  216.           NLFRefs[i].iUnRef++;
  217.         else
  218. #endif
  219.           NLFRefs[i].iRef++;
  220.  
  221.         break;
  222.       }
  223.     }
  224.   }
  225.  
  226.   return ret;
  227. }
  228.  
  229. int CEXEBuild::DefineInnerLangString(int id, int process/*=-1*/) {
  230.   bool old_keep_ref = keep_ref;
  231.  
  232.   // set reference count for NLF strings
  233.   if (keep_ref) {
  234. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  235.     if (uninstall_mode)
  236.       NLFRefs[id].iUnRef++;
  237.     else
  238. #endif
  239.       NLFRefs[id].iRef++;
  240.  
  241.     keep_ref = false;
  242.   }
  243.  
  244.   int ret = DefineLangString(NLFStrings[id].szLangStringName, process);
  245.  
  246.   keep_ref = old_keep_ref;
  247.  
  248.   return ret;
  249. }
  250.  
  251. int CEXEBuild::SetLangString(char *name, LANGID lang, char *string) {
  252.   if (!string || !name) return PS_ERROR;
  253.  
  254.   LanguageTable *table = GetLangTable(lang);
  255.   if (!table) return PS_ERROR;
  256.  
  257.   int sn;
  258.  
  259.   int pos = build_langstrings.get(name, &sn);
  260.   if (pos < 0)
  261.     pos = build_langstrings.add(name, &sn);
  262.  
  263.   if (table->lang_strings->set(sn, string))
  264.     return PS_WARNING;
  265.  
  266.   return PS_OK;
  267. }
  268.  
  269. int CEXEBuild::SetInnerString(int id, char *string) {
  270.   if ((unsigned int)id >= NLF_STRINGS || !string) return PS_ERROR;
  271.  
  272.   int ret = PS_OK;
  273.  
  274.   const char *ps = UserInnerStrings.get(id);
  275.   if (ps && *ps)
  276.     ret = PS_WARNING;
  277.  
  278.   UserInnerStrings.set(id, string);
  279.  
  280.   return ret;
  281. }
  282.  
  283. int CEXEBuild::GenerateLangTables() {
  284.   int i;
  285.   LanguageTable *lt = (LanguageTable*)lang_tables.get();
  286.  
  287.   SCRIPT_MSG("Generating language tables... ");
  288.  
  289.   if (
  290. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  291.       ubuild_langstring_num > MAX_CODED ||
  292. #endif
  293.       build_langstring_num > MAX_CODED
  294.      )
  295.   {
  296.     ERROR_MSG("\nError: too many LangStrings. Maximum allowed is %u.\n", MAX_CODED);
  297.     return PS_ERROR;
  298.   }
  299.  
  300.   // If we have no tables (user didn't set any string and didn't load any NLF) create the default one
  301.   if (!lang_tables.getlen()) {
  302.     LANGID lang = NSIS_DEFAULT_LANG;
  303.     LanguageTable *table = GetLangTable(lang);
  304.     if (!table) return PS_ERROR;
  305.  
  306.     lt = (LanguageTable*)lang_tables.get();
  307.   }
  308.  
  309.   // Apply default font
  310.   if (*build_font)
  311.   {
  312.     try {
  313.       init_res_editor();
  314.  
  315. #define ADD_FONT(id) { \
  316.         BYTE* dlg = res_editor->GetResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); \
  317.         if (dlg) { \
  318.           CDialogTemplate td(dlg); \
  319.           free(dlg); \
  320.           td.SetFont(build_font, build_font_size); \
  321.           DWORD dwSize; \
  322.           dlg = td.Save(dwSize); \
  323.           res_editor->UpdateResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), dlg, dwSize); \
  324.           res_editor->FreeResource(dlg); \
  325.         } \
  326.       }
  327.  
  328. #ifdef NSIS_CONFIG_LICENSEPAGE
  329.       ADD_FONT(IDD_LICENSE);
  330.       ADD_FONT(IDD_LICENSE_FSRB);
  331.       ADD_FONT(IDD_LICENSE_FSCB);
  332. #endif
  333.       ADD_FONT(IDD_DIR);
  334. #ifdef NSIS_CONFIG_COMPONENTPAGE
  335.       ADD_FONT(IDD_SELCOM);
  336. #endif
  337.       ADD_FONT(IDD_INST);
  338.       ADD_FONT(IDD_INSTFILES);
  339. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  340.       ADD_FONT(IDD_UNINST);
  341. #endif
  342. #ifdef NSIS_CONFIG_CRC_SUPPORT
  343.       ADD_FONT(IDD_VERIFY);
  344. #endif
  345. #undef ADD_FONT
  346.     }
  347.     catch (exception& err) {
  348.       ERROR_MSG("\nError while applying font: %s\n", err.what());
  349.       return PS_ERROR;
  350.     }
  351.   }
  352.  
  353.   // Fill tables with defaults (if needed) and with instruction strings
  354.   // Create language specific resources (currently only dialogs with different fonts)
  355.   int num_lang_tables = lang_tables.getlen() / sizeof(LanguageTable);
  356.   // if there is one string table then there is no need for two sets of dialogs
  357.   int cur_offset = num_lang_tables == 1 ? 0 : 100;
  358.   for (i = 0; i < num_lang_tables; i++)
  359.   {
  360.     if ((lt[i].nlf.m_szFont && !*build_font) || lt[i].nlf.m_bRTL)
  361.     {
  362.       lt[i].dlg_offset = cur_offset;
  363.  
  364.       char *font = lt[i].nlf.m_szFont;
  365.       if (*build_font) font = 0;
  366.  
  367.       try {
  368.         init_res_editor();
  369.  
  370. #define ADD_FONT(id) { \
  371.           BYTE* dlg = res_editor->GetResource(RT_DIALOG, MAKEINTRESOURCE(id), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)); \
  372.           if (dlg) { \
  373.             CDialogTemplate td(dlg,lt[i].nlf.m_uCodePage); \
  374.             free(dlg); \
  375.             if (font) td.SetFont(font, lt[i].nlf.m_iFontSize); \
  376.             if (lt[i].nlf.m_bRTL) td.ConvertToRTL(); \
  377.             DWORD dwSize; \
  378.             dlg = td.Save(dwSize); \
  379.             res_editor->UpdateResource(RT_DIALOG, MAKEINTRESOURCE(id+cur_offset), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), dlg, dwSize); \
  380.             res_editor->FreeResource(dlg); \
  381.           } \
  382.         }
  383.  
  384. #ifdef NSIS_CONFIG_LICENSEPAGE
  385.         ADD_FONT(IDD_LICENSE);
  386.         ADD_FONT(IDD_LICENSE_FSRB);
  387.         ADD_FONT(IDD_LICENSE_FSCB);
  388. #endif
  389.         ADD_FONT(IDD_DIR);
  390. #ifdef NSIS_CONFIG_COMPONENTPAGE
  391.         ADD_FONT(IDD_SELCOM);
  392. #endif
  393.         ADD_FONT(IDD_INST);
  394.         ADD_FONT(IDD_INSTFILES);
  395. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  396.         ADD_FONT(IDD_UNINST);
  397. #endif
  398. #ifdef NSIS_CONFIG_CRC_SUPPORT
  399.         ADD_FONT(IDD_VERIFY);
  400. #endif
  401. #undef ADD_FONT
  402.       }
  403.       catch (exception& err) {
  404.         ERROR_MSG("\nError while applying NLF font/RTL: %s\n", err.what());
  405.         return PS_ERROR;
  406.       }
  407.  
  408.       cur_offset += 100;
  409.     }
  410.   }
  411.  
  412.   // Add all installer language strings
  413.   int j, l, tabsset;
  414.   struct langstring* lang_strings;
  415.   TinyGrowBuf *string_ptrs = new TinyGrowBuf[num_lang_tables];
  416.  
  417.   tabsset = 1;
  418.   while (tabsset)
  419.   {
  420.     tabsset = 0;
  421.     for (i = num_lang_tables; i--; )
  422.     {
  423.       // Fill in default values for all used language strings that we can
  424.       FillLanguageTable(<[i]);
  425.       // Make sure the string lists are large enough
  426.       string_ptrs[i].set_zeroing(1);
  427.       string_ptrs[i].resize(build_langstring_num * sizeof(int));
  428.     }
  429.  
  430.     // For all current language strings
  431.     lang_strings = build_langstrings.sort_index(&l);
  432.     for (j = 0; j < l; j++)
  433.     {
  434.       // Is this language string used (in the installer)?
  435.       if (lang_strings[j].index >= 0)
  436.       {
  437.         // For each language
  438.         for (i = num_lang_tables; i--; )
  439.         {
  440.           // Get the current string pointer
  441.           int *ptr = (int *)string_ptrs[i].get() + lang_strings[j].index;
  442.           // Not already set?
  443.           if (!*ptr)
  444.           {
  445.             // Get the language string and its name
  446.             const char *str = lt[i].lang_strings->get(lang_strings[j].sn);
  447.             const char *lsn = build_langstrings.offset2name(lang_strings[j].name);
  448.             if (!str || !*str)
  449.             {
  450.               // No string is defined; give a warning (for user strings only)
  451.               if (lsn[0] != '^')
  452.                 warning("LangString \"%s\" is not set in language table of language %d", lsn, lt[i].lang_id);
  453.             }
  454.             else
  455.             {
  456.               // Add the language string to the string data block
  457.               char fn[1024];
  458.               sprintf(fn, "LangString %s", lsn);
  459.               curfilename = fn;
  460.               linecnt = lt[i].lang_id;
  461.               *ptr = add_string(str, lang_strings[j].process, lt[i].nlf.m_uCodePage);
  462.               curfilename = 0;
  463.               // Indicate that we should check again for any newly referenced language strings
  464.               tabsset++;
  465.             }
  466.           }
  467.         }
  468.       }
  469.     }
  470.   }
  471.  
  472.   // Optimize langstrings and check for recursion
  473.   for (i = num_lang_tables; i--; )
  474.   {
  475.     TinyGrowBuf rec;
  476.     int *lst = (int *)string_ptrs[i].get();
  477.     for (j = 0; j < build_langstring_num; j++)
  478.     {
  479.       // Does this string reference another language string directly?
  480.       while (lst[j] < 0)
  481.       {
  482.         // Search through list of language string references
  483.         for (l = 0; (unsigned int)l < rec.getlen() / sizeof(int); l++)
  484.         {
  485.           if (((int*)rec.get())[l] == lst[j])
  486.           {
  487.             // We have the index of a recursive language string; now find the name
  488.             const char *name = "(unnamed)";
  489.             for (l = 0; l < build_langstring_num; l++)
  490.               if (lang_strings[l].index == j)
  491.                 name = build_langstrings.offset2name(lang_strings[l].name);
  492.             ERROR_MSG("Error: LangString %s is recursive!\n", name);
  493.             delete [] string_ptrs;
  494.             return PS_ERROR;
  495.           }
  496.         }
  497.         // Add this reference to the list
  498.         rec.add(&lst[j], sizeof(int));
  499.         // and dereference it
  500.         lst[j] = lst[-lst[j] - 1];
  501.       }
  502.       rec.resize(0);
  503.     }
  504.   }
  505.  
  506.   // Add language tables into their datablock
  507.   for (i = num_lang_tables; i--; )
  508.   {
  509.     build_langtables.add(<[i].lang_id, sizeof(LANGID));
  510.     build_langtables.add(<[i].dlg_offset, sizeof(int));
  511.     int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
  512.     build_langtables.add(&rtl, sizeof(int));
  513.     build_langtables.add(string_ptrs[i].get(), string_ptrs[i].getlen());
  514.     string_ptrs[i].resize(0);
  515.   }
  516.  
  517.   build_header.blocks[NB_LANGTABLES].num = num_lang_tables;
  518.   build_header.langtable_size = build_langtables.getlen() / num_lang_tables;
  519.  
  520. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  521.   // Now do it all again, this time for the uninstaller
  522.   set_uninstall_mode(1);
  523.  
  524.   tabsset = 1;
  525.   while (tabsset)
  526.   {
  527.     tabsset = 0;
  528.     for (i = num_lang_tables; i--; )
  529.     {
  530.       // Fill in default values for all used language strings that we can
  531.       FillLanguageTable(<[i]);
  532.       // Make sure the string lists are large enough
  533.       string_ptrs[i].set_zeroing(1);
  534.       string_ptrs[i].resize(ubuild_langstring_num * sizeof(int));
  535.     }
  536.  
  537.     // For all current language strings
  538.     lang_strings = build_langstrings.sort_uindex(&l);
  539.     for (j = 0; j < l; j++)
  540.     {
  541.       // Is this language string used (in the uninstaller)?
  542.       if (lang_strings[j].uindex >= 0)
  543.       {
  544.         // For each language
  545.         for (i = num_lang_tables; i--; )
  546.         {
  547.           // Get the current string pointer
  548.           int *ptr = (int *)string_ptrs[i].get() + lang_strings[j].uindex;
  549.           // Not already set?
  550.           if (!*ptr)
  551.           {
  552.             // Get the language string and its name
  553.             const char *str = lt[i].lang_strings->get(lang_strings[j].sn);
  554.             const char *lsn = build_langstrings.offset2name(lang_strings[j].name);
  555.             if (!str || !*str)
  556.             {
  557.               // No string is defined; give a warning (for user strings only)
  558.               if (lsn[0] != '^')
  559.                 warning("LangString \"%s\" is not set in language table of language %d", lsn, lt[i].lang_id);
  560.             }
  561.             else
  562.             {
  563.               // Add the language string to the string data block
  564.               char fn[1024];
  565.               sprintf(fn, "LangString %s", lsn);
  566.               curfilename = fn;
  567.               linecnt = lt[i].lang_id;
  568.               *ptr = add_string(str, lang_strings[j].process, lt[i].nlf.m_uCodePage);
  569.               curfilename = 0;
  570.               // Indicate that we should check again for any newly referenced language strings
  571.               tabsset++;
  572.             }
  573.           }
  574.         }
  575.       }
  576.     }
  577.   }
  578.  
  579.   // Optimize langstrings and check for recursion
  580.   for (i = num_lang_tables; i--; )
  581.   {
  582.     TinyGrowBuf rec;
  583.     int *lst = (int *)string_ptrs[i].get();
  584.     for (j = 0; j < ubuild_langstring_num; j++)
  585.     {
  586.       // Does this string reference another language string directly?
  587.       while (lst[j] < 0)
  588.       {
  589.         // Search through list of language string references
  590.         for (l = 0; (unsigned int)l < rec.getlen() / sizeof(int); l++)
  591.         {
  592.           if (((int*)rec.get())[l] == lst[j])
  593.           {
  594.             // We have the index of a recursive language string; now find the name
  595.             const char *name = "(unnamed)";
  596.             for (l = 0; l < ubuild_langstring_num; l++)
  597.               if (lang_strings[l].uindex == j)
  598.                 name = build_langstrings.offset2name(lang_strings[l].name);
  599.             ERROR_MSG("Error: LangString %s is recursive!\n", name);
  600.             delete [] string_ptrs;
  601.             return PS_ERROR;
  602.           }
  603.         }
  604.         // Add this reference to the list
  605.         rec.add(&lst[j], sizeof(int));
  606.         // and dereference it
  607.         lst[j] = lst[-lst[j] - 1];
  608.       }
  609.       rec.resize(0);
  610.     }
  611.   }
  612.  
  613.   // Add language tables into their datablock
  614.   for (i = num_lang_tables; i--; )
  615.   {
  616.     ubuild_langtables.add(<[i].lang_id, sizeof(LANGID));
  617.     ubuild_langtables.add(<[i].dlg_offset, sizeof(int));
  618.     int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
  619.     ubuild_langtables.add(&rtl, sizeof(int));
  620.     ubuild_langtables.add(string_ptrs[i].get(), string_ptrs[i].getlen());
  621.     string_ptrs[i].resize(0);
  622.   }
  623.  
  624.   build_uninst.blocks[NB_LANGTABLES].num = num_lang_tables;
  625.   build_uninst.langtable_size = ubuild_langtables.getlen() / num_lang_tables;
  626.  
  627.   set_uninstall_mode(0);
  628. #endif
  629.  
  630.   SCRIPT_MSG("Done!\n");
  631.  
  632.   delete [] string_ptrs;
  633.   return PS_OK;
  634. }
  635.  
  636. void CEXEBuild::FillLanguageTable(LanguageTable *table) {
  637.   for (int i = 0; i < NLF_STRINGS; i++) {
  638. #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
  639.     if (!NLFRefs[i].iUnRef && !NLFRefs[i].iRef)
  640.       continue;
  641. #else
  642.     if (!NLFRefs[i].iRef)
  643.       continue;
  644. #endif
  645.  
  646.     else if (i == NLF_SPACE_REQ || i == NLF_SPACE_AVAIL)
  647.     {
  648.       if (no_space_texts)
  649.       {
  650.         continue;
  651.       }
  652.     }
  653.  
  654.     int sn, index;
  655.     int pos = build_langstrings.get(NLFStrings[i].szLangStringName, &sn, &index);
  656.     if (pos >= 0) {
  657.       const char *str = table->lang_strings->get(sn);
  658.       if (!str || !*str) {
  659.         const char *us = UserInnerStrings.get(i);
  660.         if (i == NLF_NAME_DA && (!us || !*us))
  661.         {
  662.           // if the user didn't set NLF_NAME_DA we set it to $(^Name)
  663.           table->lang_strings->set(sn, "$(^Name)");
  664.         }
  665.         if (us && *us) {
  666.           table->lang_strings->set(sn, (char *) us);
  667.         }
  668.         else {
  669.           char *dstr = table->nlf.m_szStrings[i] ? table->nlf.m_szStrings[i] : NLFStrings[i].szDefault;
  670.           if (!dstr)
  671.             continue;
  672.           if (i == NLF_BRANDING) {
  673.             char temp[NSIS_MAX_STRLEN + sizeof(NSIS_VERSION)];
  674.             sprintf(temp, dstr, NSIS_VERSION);
  675.             table->lang_strings->set(sn, temp);
  676.             continue;
  677.           }
  678.           else if (i == NLF_FONT)
  679.           {
  680.             char *font = *build_font ? build_font : table->nlf.m_szFont;
  681.             if (font)
  682.               table->lang_strings->set(sn, font);
  683.             else
  684.               table->lang_strings->set(sn, dstr);
  685.             continue;
  686.           }
  687.           else if (i == NLF_FONTSIZE)
  688.           {
  689.             int font_size = *build_font ? build_font_size : table->nlf.m_iFontSize;
  690.             if (font_size)
  691.             {
  692.               char temp[64];
  693.               sprintf(temp, "%d", font_size);
  694.               table->lang_strings->set(sn, temp);
  695.             }
  696.             else
  697.               table->lang_strings->set(sn, dstr);
  698.             continue;
  699.           }
  700.           table->lang_strings->set(sn, dstr);
  701.         }
  702.       }
  703.     }
  704.   }
  705. }
  706.  
  707. char SkipComments(FILE *f) {
  708.   char c;
  709.   while (c = fgetc(f)) {
  710.     while (c == '\n' || c == '\r') {
  711.       c = fgetc(f); // Skip empty lines
  712.     }
  713.     if (c == '#' || c == ';') {
  714.       while (c = fgetc(f)) {
  715.        if (c == '\n') break;
  716.       }
  717.     }
  718.     else break;
  719.   }
  720.   return c;
  721. }
  722.  
  723. // NSIS Language File parser
  724. LanguageTable * CEXEBuild::LoadLangFile(char *filename) {
  725.   FILE *f = fopen(filename, "r");
  726.   if (!f) {
  727.     ERROR_MSG("Error: Can't open language file!\n");
  728.     return 0;
  729.   }
  730.  
  731.   // Check header
  732.   char buf[NSIS_MAX_STRLEN];
  733.   buf[0] = SkipComments(f);
  734.   fgets(buf+1, NSIS_MAX_STRLEN, f);
  735.  
  736.   if (strncmp(buf, "NLF v", 5)) {
  737.     ERROR_MSG("Error: Invalid language file.\n");
  738.     return 0;
  739.   }
  740.   int nlf_version = atoi(buf+5);
  741.   if (nlf_version != NLF_VERSION) {
  742.     if (nlf_version != 2 && nlf_version != 3 && nlf_version != 4 && nlf_version != 5) {
  743.       ERROR_MSG("Error: Language file version doesn't match NSIS version.\n");
  744.       return 0;
  745.     }
  746.   }
  747.  
  748.   // Get language ID
  749.   buf[0] = SkipComments(f);
  750.   fgets(buf+1, NSIS_MAX_STRLEN, f);
  751.   LANGID lang_id = atoi(buf);
  752.  
  753.   // Get appropriate table
  754.   LanguageTable *table = GetLangTable(lang_id);
  755.   if (!table)
  756.     return 0;
  757.  
  758.   NLF *nlf = &table->nlf;
  759.  
  760.   if (nlf->m_bLoaded) {
  761.     ERROR_MSG("Error: can't load same language file twice.\n");
  762.     return 0;
  763.   }
  764.  
  765.   // Generate language name
  766.   char *p, *p2, t;
  767.  
  768.   p = strrchr(filename, '.');
  769.   if (p) {
  770.     t = *p;
  771.     *p = 0;
  772.   }
  773.   p2 = strrchr(filename, '\\');
  774.   if (p2) {
  775.     p2++;
  776.     nlf->m_szName = (char*)malloc(strlen(p2)+1);
  777.     strcpy(nlf->m_szName, p2);
  778.   }
  779.   else {
  780.     nlf->m_szName = (char*)malloc(strlen(filename)+1);
  781.     strcpy(nlf->m_szName, filename);
  782.   }
  783.   if (p) *p = t;
  784.  
  785.   if (nlf_version != NLF_VERSION) {
  786.     warning_fl("%s language file version doesn't match. Using default English texts for missing strings.", nlf->m_szName);
  787.   }
  788.  
  789.   int temp;
  790.  
  791.   // Get font
  792.   buf[0] = SkipComments(f);
  793.   fgets(buf+1, NSIS_MAX_STRLEN, f);
  794.   if (!nlf->m_szFont) {
  795.     temp=strlen(buf);
  796.     while (buf[temp-1] == '\n' || buf[temp-1] == '\r') {
  797.       buf[temp-1] = 0;
  798.       temp--;
  799.     }
  800.     if (buf[0] != '-' || buf[1] != 0) {
  801.       nlf->m_szFont = (char*)malloc(strlen(buf)+1);
  802.       strcpy(nlf->m_szFont, buf);
  803.     }
  804.   }
  805.  
  806.   buf[0] = SkipComments(f);
  807.   fgets(buf+1, NSIS_MAX_STRLEN, f);
  808.   if (!nlf->m_iFontSize) {
  809.     if (buf[0] != '-' || buf[1] != 0) {
  810.       nlf->m_iFontSize = atoi(buf);
  811.     }
  812.   }
  813.  
  814.   // Get code page
  815.   nlf->m_uCodePage = CP_ACP;
  816.   buf[0] = SkipComments(f);
  817.   fgets(buf+1, NSIS_MAX_STRLEN, f);
  818.   if (buf[0] != '-' || buf[1] != 0) {
  819.     nlf->m_uCodePage = atoi(buf);
  820.     if (!IsValidCodePage(nlf->m_uCodePage))
  821.       nlf->m_uCodePage = CP_ACP;
  822.   }
  823.  
  824.   // Get RTL setting
  825.   nlf->m_szStrings[NLF_RTL] = (char *)malloc(2);
  826.   nlf->m_bRTL = false;
  827.   buf[0] = SkipComments(f);
  828.   fgets(buf+1, NSIS_MAX_STRLEN, f);
  829.   if (buf[0] == 'R' && buf[1] == 'T' && buf[2] == 'L' && (!buf[3] || buf[3] == '\r' || buf[3] == '\n')) {
  830.     nlf->m_bRTL = true;
  831.     strcpy(nlf->m_szStrings[NLF_RTL], "1");
  832.   }
  833.   else {
  834.     strcpy(nlf->m_szStrings[NLF_RTL], "0");
  835.   }
  836.  
  837.   // Read strings
  838.   for (int i = 0; i < NLF_STRINGS - 3 /* ^Font, ^FontSize and ^RTL */; i++) {
  839.  
  840.     // skip virtual strings
  841.     if (!NLFStrings[i].szDefault)
  842.       continue;
  843.  
  844.     // Fill in for missing strings
  845.     // 0 will mean default will be used from NLFStrings
  846.     switch (i) {
  847.       case NLF_BTN_LICENSE_AGREE:
  848.       case NLF_BTN_LICENSE_DISAGREE:
  849.         if (nlf_version >= 3) break;
  850.       case NLF_LOG_INSTALL_PROCESS:
  851.       case NLF_BYTE:
  852.       case NLF_KILO:
  853.       case NLF_MEGA:
  854.       case NLF_GIGA:
  855.       case NLF_REGISTERING:
  856.       case NLF_UNREGISTERING:
  857.         if (nlf_version >= 4) break;
  858.       case NLF_FILE_ERROR_NOIGNORE:
  859.         if (nlf_version >= 5) break;
  860.       case NLF_USUBCAPTION_OPTIONS:
  861.       case NLF_USUBCAPTION_DIR:
  862.       case NLF_CLICK_NEXT:
  863.       case NLF_CLICK_INSTALL:
  864.       case NLF_CLICK_UNINSTALL:
  865.       case NLF_LICENSE_TEXT:
  866.       case NLF_LICENSE_TEXT_FSCB:
  867.       case NLF_LICENSE_TEXT_FSRB:
  868.       case NLF_ULICENSE_TEXT:
  869.       case NLF_ULICENSE_TEXT_FSCB:
  870.       case NLF_ULICENSE_TEXT_FSRB:
  871.       case NLF_COMP_TEXT:
  872.       case NLF_UCOMP_TEXT:
  873.       case NLF_UCOMP_SUBTEXT1:
  874.       case NLF_UCOMP_SUBTEXT1_NO_INST_TYPES:
  875.       case NLF_UCOMP_SUBTEXT2:
  876.       case NLF_DIR_TEXT:
  877.       case NLF_DIR_BROWSETEXT:
  878.       case NLF_UDIR_TEXT:
  879.       case NLF_UDIR_SUBTEXT:
  880.       case NLF_UDIR_BROWSETEXT:
  881.       case NLF_UNINST_TEXT:
  882.         if (nlf_version >= 6) break;
  883.         nlf->m_szStrings[i] = 0;
  884.         continue;
  885.     }
  886.  
  887.     buf[0] = SkipComments(f);
  888.  
  889.     fgets(buf+1, NSIS_MAX_STRLEN, f);
  890.     if (strlen(buf) == NSIS_MAX_STRLEN-1) {
  891.       ERROR_MSG("Error: String too long (string #%d - \"%s\")", i, NLFStrings[i].szLangStringName);
  892.       return 0;
  893.     }
  894.     temp=strlen(buf);
  895.  
  896.     while (buf[temp-1] == '\n' || buf[temp-1] == '\r') {
  897.       buf[--temp] = 0;
  898.     }
  899.  
  900.     char *in = buf;
  901.  
  902.     // trim quotes
  903.     if (buf[0] == '"' && buf[temp-1] == '"') {
  904.       in++;
  905.       buf[--temp] = 0;
  906.     }
  907.  
  908.     nlf->m_szStrings[i] = (char*)malloc(temp+1);
  909.     for (char *out = nlf->m_szStrings[i]; *in; in++, out++) {
  910.       if (*in == '\\') {
  911.         in++;
  912.         switch (*in) {
  913.           case 'n':
  914.             *out = '\n';
  915.             break;
  916.           case 'r':
  917.             *out = '\r';
  918.             break;
  919.           case 't':
  920.             *out = '\t';
  921.             break;
  922.           default:
  923.             *out++ = '\\';
  924.             *out = *in;
  925.         }
  926.       }
  927.       else *out = *in;
  928.     }
  929.     *out = 0;
  930.   }
  931.   fclose(f);
  932.  
  933.   nlf->m_bLoaded = true;
  934.  
  935.   return table;
  936. }
  937.  
  938. void CEXEBuild::DeleteLangTable(LanguageTable *table) {
  939.   if (table->nlf.m_szName)
  940.     free(table->nlf.m_szName);
  941.   if (table->nlf.m_szFont)
  942.     free(table->nlf.m_szFont);
  943.   delete table->lang_strings;
  944.   for (int i = 0; i < NLF_STRINGS; i++) {
  945.     if (table->nlf.m_szStrings[i])
  946.       free(table->nlf.m_szStrings[i]);
  947.   }
  948. }
  949.