NSIS/Source/lang.cpp
anders_k 7cc150c464 MakeNSIS can now generate Unicode or Ansi installers based on a script attribute. SCons generates both Ansi and Unicode stubs and plugins.
The official plugins are now stored in architecture specific subdirectories under NSIS\Plugins. !AddPluginDir also gained a new (optional) architecture flag because MakeNSIS now stores separate plugin information for each target architecture. Storing plugins in the root of the Plugins directory is no longer supported.

MinGW does not implement the unicode CRT startup functions so the entry point functions and linker parameters had to be changed. The unicode tools use the ansi entry point and a small helper function that calls into the real code: _tmain has full argc+argv emulation while wWinMain does not pass the command line parameters. The stubs do not use any CRT functions and have no CRT or unicode helper code, they call our entry point directly.



git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6269 212acab6-be3b-0410-9dea-997c60f758d6
2012-10-13 01:47:50 +00:00

1219 lines
37 KiB
C++

/*
* lang.cpp
*
* This file is a part of NSIS.
*
* Copyright (C) 1999-2009 Nullsoft and Contributors
*
* Licensed under the zlib/libpng license (the "License");
* you may not use this file except in compliance with the License.
*
* Licence details can be found in the file COPYING.
*
* This software is provided 'as-is', without any express or implied
* warranty.
*
* Unicode support and Doxygen comments by Jim Park -- 07/25/2007
*/
#include "Platform.h"
#include <stdio.h>
#include <stdlib.h>
#include "tchar.h"
#include "build.h"
#include "util.h"
#include "DialogTemplate.h"
#include "exehead/resource.h"
#include <nsis-version.h>
#include "tstring.h"
#include "utf.h"
using namespace std;
// Default English strings. Should match NSIS_DEFAULT_LANG
// Do not change the first string in every item, it's the LangString
// name for usage in scripts.
typedef enum {
NONE_STATIC = 0,
INSTALL_STATIC = 1,
UNINSTALL_STATIC = 2,
BOTH_STATIC = 3
} STATICID;
struct NLFString {
const TCHAR *szLangStringName;
const TCHAR *szDefault;
STATICID eStaticID;
};
NLFString NLFStrings[NLF_STRINGS] = {
{_T("^Branding"), _T("Nullsoft Install System %s"), BOTH_STATIC},
{_T("^SetupCaption"), _T("$(^Name) Setup"), INSTALL_STATIC},
{_T("^UninstallCaption"), _T("$(^Name) Uninstall"), UNINSTALL_STATIC},
{_T("^LicenseSubCaption"), _T(": License Agreement"), NONE_STATIC},
{_T("^ComponentsSubCaption"), _T(": Installation Options"), NONE_STATIC},
{_T("^DirSubCaption"), _T(": Installation Folder"), NONE_STATIC},
{_T("^InstallingSubCaption"), _T(": Installing"), NONE_STATIC},
{_T("^CompletedSubCaption"), _T(": Completed"), NONE_STATIC},
{_T("^UnComponentsSubCaption"), _T(": Uninstallation Options"), NONE_STATIC},
{_T("^UnDirSubCaption"), _T(": Uninstallation Folder"), NONE_STATIC},
{_T("^ConfirmSubCaption"), _T(": Confirmation"), NONE_STATIC},
{_T("^UninstallingSubCaption"), _T(": Uninstalling"), NONE_STATIC},
{_T("^UnCompletedSubCaption"), _T(": Completed"), NONE_STATIC},
{_T("^BackBtn"), _T("< &Back"), NONE_STATIC},
{_T("^NextBtn"), _T("&Next >"), NONE_STATIC},
{_T("^AgreeBtn"), _T("I &Agree"), NONE_STATIC},
{_T("^AcceptBtn"), _T("I &accept the terms of the License Agreement"), NONE_STATIC},
{_T("^DontAcceptBtn"), _T("I &do not accept the terms of the License Agreement"), NONE_STATIC},
{_T("^InstallBtn"), _T("&Install"), NONE_STATIC},
{_T("^UninstallBtn"), _T("&Uninstall"), NONE_STATIC},
{_T("^CancelBtn"), _T("Cancel"), NONE_STATIC},
{_T("^CloseBtn"), _T("&Close"), NONE_STATIC},
{_T("^BrowseBtn"), _T("B&rowse..."), NONE_STATIC},
{_T("^ShowDetailsBtn"), _T("Show &details"), NONE_STATIC},
{_T("^ClickNext"), _T("Click Next to continue."), NONE_STATIC},
{_T("^ClickInstall"), _T("Click Install to start the installation."), NONE_STATIC},
{_T("^ClickUninstall"), _T("Click Uninstall to start the uninstallation."), NONE_STATIC},
{_T("^Name"), _T("Name"), BOTH_STATIC},
{_T("^NameDA"), 0, NONE_STATIC}, // virtual
{_T("^Completed"), _T("Completed"), NONE_STATIC},
{_T("^LicenseText"), _T("Please review the license agreement before installing $(^NameDA). If you accept all terms of the agreement, click I Agree."), NONE_STATIC},
{_T("^LicenseTextCB"), _T("Please review the license agreement before installing $(^NameDA). If you accept all terms of the agreement, click the check box below. $_CLICK"), NONE_STATIC},
{_T("^LicenseTextRB"), _T("Please review the license agreement before installing $(^NameDA). If you accept all terms of the agreement, select the first option below. $_CLICK"), NONE_STATIC},
{_T("^UnLicenseText"), _T("Please review the license agreement before uninstalling $(^NameDA). If you accept all terms of the agreement, click I Agree."), NONE_STATIC},
{_T("^UnLicenseTextCB"), _T("Please review the license agreement before uninstalling $(^NameDA). If you accept all terms of the agreement, click the check box below. $_CLICK"), NONE_STATIC},
{_T("^UnLicenseTextRB"), _T("Please review the license agreement before uninstalling $(^NameDA). If you accept all terms of the agreement, select the first option below. $_CLICK"), NONE_STATIC},
{_T("^LicenseData"), 0, NONE_STATIC}, // virtual - not processed
{_T("^Custom"), _T("Custom"), NONE_STATIC},
{_T("^ComponentsText"), _T("Check the components you want to install and uncheck the components you don't want to install. $_CLICK"), NONE_STATIC},
{_T("^ComponentsSubText1"), _T("Select the type of install:"), NONE_STATIC},
{_T("^ComponentsSubText2_NoInstTypes"), _T("Select components to install:"), NONE_STATIC},
{_T("^ComponentsSubText2"), _T("Or, select the optional components you wish to install:"), NONE_STATIC},
{_T("^UnComponentsText"), _T("Check the components you want to uninstall and uncheck the components you don't want to uninstall. $_CLICK"), NONE_STATIC},
{_T("^UnComponentsSubText1"), _T("Select the type of uninstall:"), NONE_STATIC},
{_T("^UnComponentsSubText2_NoInstTypes"), _T("Select components to uninstall:"), NONE_STATIC},
{_T("^UnComponentsSubText2"), _T("Or, select the optional components you wish to uninstall:"), NONE_STATIC},
{_T("^DirText"), _T("Setup will install $(^NameDA) in the following folder. To install in a different folder, click Browse and select another folder. $_CLICK"), NONE_STATIC},
{_T("^DirSubText"), _T("Destination Folder"), NONE_STATIC},
{_T("^DirBrowseText"), _T("Select the folder to install $(^NameDA) in:"), NONE_STATIC},
{_T("^UnDirText"), _T("Setup will uninstall $(^NameDA) from the following folder. To uninstall from a different folder, click Browse and select another folder. $_CLICK"), NONE_STATIC},
{_T("^UnDirSubText"), _T(""), NONE_STATIC},
{_T("^UnDirBrowseText"), _T("Select the folder to uninstall $(^NameDA) from:"), NONE_STATIC},
{_T("^SpaceAvailable"), _T("Space available: "), BOTH_STATIC},
{_T("^SpaceRequired"), _T("Space required: "), BOTH_STATIC},
{_T("^UninstallingText"), _T("This wizard will uninstall $(^NameDA) from your computer. $_CLICK"), NONE_STATIC},
{_T("^UninstallingSubText"), _T("Uninstalling from:"), NONE_STATIC},
{_T("^FileError"), _T("Error opening file for writing: \r\n\r\n$0\r\n\r\nClick Abort to stop the installation,\r\nRetry to try again, or\r\nIgnore to skip this file."), NONE_STATIC},
{_T("^FileError_NoIgnore"), _T("Error opening file for writing: \r\n\r\n$0\r\n\r\nClick Retry to try again, or\r\nCancel to stop the installation."), NONE_STATIC},
{_T("^CantWrite"), _T("Can't write: "), BOTH_STATIC},
{_T("^CopyFailed"), _T("Copy failed"), BOTH_STATIC},
{_T("^CopyTo"), _T("Copy to "), BOTH_STATIC},
{_T("^Registering"), _T("Registering: "), NONE_STATIC},
{_T("^Unregistering"), _T("Unregistering: "), NONE_STATIC},
{_T("^SymbolNotFound"), _T("Could not find symbol: "), BOTH_STATIC},
{_T("^CouldNotLoad"), _T("Could not load: "), BOTH_STATIC},
{_T("^CreateFolder"), _T("Create folder: "), BOTH_STATIC},
{_T("^CreateShortcut"), _T("Create shortcut: "), BOTH_STATIC},
{_T("^CreatedUninstaller"), _T("Created uninstaller: "), BOTH_STATIC},
{_T("^Delete"), _T("Delete file: "), BOTH_STATIC},
{_T("^DeleteOnReboot"), _T("Delete on reboot: "), BOTH_STATIC},
{_T("^ErrorCreatingShortcut"), _T("Error creating shortcut: "), BOTH_STATIC},
{_T("^ErrorCreating"), _T("Error creating: "), BOTH_STATIC},
{_T("^ErrorDecompressing"), _T("Error decompressing data! Corrupted installer?"), BOTH_STATIC},
{_T("^ErrorRegistering"), _T("Error registering DLL"), BOTH_STATIC},
{_T("^ExecShell"), _T("ExecShell: "), BOTH_STATIC},
{_T("^Exec"), _T("Execute: "), BOTH_STATIC},
{_T("^Extract"), _T("Extract: "), BOTH_STATIC},
{_T("^ErrorWriting"), _T("Extract: error writing to file "), BOTH_STATIC},
{_T("^InvalidOpcode"), _T("Installer corrupted: invalid opcode"), BOTH_STATIC},
{_T("^NoOLE"), _T("No OLE for: "), BOTH_STATIC},
{_T("^OutputFolder"), _T("Output folder: "), BOTH_STATIC},
{_T("^RemoveFolder"), _T("Remove folder: "), BOTH_STATIC},
{_T("^RenameOnReboot"), _T("Rename on reboot: "), BOTH_STATIC},
{_T("^Rename"), _T("Rename: "), BOTH_STATIC},
{_T("^Skipped"), _T("Skipped: "), BOTH_STATIC},
{_T("^CopyDetails"), _T("Copy Details To Clipboard"), BOTH_STATIC},
{_T("^LogInstall"), _T("Log install process"), BOTH_STATIC},
{_T("^Byte"), _T("B"), BOTH_STATIC},
{_T("^Kilo"), _T("K"), BOTH_STATIC},
{_T("^Mega"), _T("M"), BOTH_STATIC},
{_T("^Giga"), _T("G"), BOTH_STATIC},
{_T("^Font"), _T("MS Shell Dlg"), NONE_STATIC},
{_T("^FontSize"), _T("8"), NONE_STATIC},
{_T("^RTL"), _T("0"), NONE_STATIC},
{_T("^Language"), _T("English"), NONE_STATIC}
};
// ==============
// LangStringList
// ==============
int LangStringList::add(const TCHAR *name, int *sn/*=0*/)
{
int pos = SortedStringListND<struct langstring>::add(name);
if (pos == -1) return -1;
langstring* lstrPtr = (langstring*)(m_gr.get()) + pos;
lstrPtr->sn = m_count;
if (sn) *sn = m_count;
m_count++;
lstrPtr->index = -1;
lstrPtr->uindex = -1;
lstrPtr->process = 1;
return pos;
}
int LangStringList::get(const TCHAR *name, int *sn/*=0*/, int *index/*=0*/, int *uindex/*=0*/, int *process/*=0*/)
{
if (index) *index = -1;
if (uindex) *uindex = -1;
if (sn) *sn = -1;
int v=find(name);
if (v==-1) return -1;
langstring* lstrPtr = (langstring*)(m_gr.get()) + v;
if (index) *index = lstrPtr->index;
if (uindex) *uindex = lstrPtr->uindex;
if (sn) *sn = lstrPtr->sn;
if (process) *process = lstrPtr->process;
return v;
}
void LangStringList::set(int pos, int index/*=-1*/, int uindex/*=-1*/, int process/*=-1*/)
{
if ((unsigned int)pos > (m_gr.getlen() / sizeof(struct langstring)))
return;
struct langstring *data=((struct langstring *) m_gr.get()) + pos;
if (index >= 0) data->index = index;
if (uindex >= 0) data->uindex = uindex;
if (process >= 0) data->process = process;
}
void LangStringList::set(const TCHAR *name, int index, int uindex/*=-1*/, int process/*=-1*/)
{
set(find(name), index, uindex, process);
}
const TCHAR* LangStringList::pos2name(int pos)
{
struct langstring *data=(struct langstring *) m_gr.get();
if (pos < 0 || (unsigned int)pos > (m_gr.getlen() / sizeof(struct langstring)))
return 0;
return ((const TCHAR*) m_strings.get() + data[pos].name);
}
const TCHAR* LangStringList::offset2name(int name)
{
if (name < 0 || (unsigned int)name > m_strings.getlen()/sizeof(TCHAR))
return 0;
return (const TCHAR*) m_strings.get() + name;
}
int LangStringList::getnum()
{
return m_gr.getlen() / sizeof(struct langstring);
}
int LangStringList::compare_index(const void *item1, const void *item2)
{
struct langstring *ls1 = (struct langstring *)item1;
struct langstring *ls2 = (struct langstring *)item2;
return ls1->index - ls2->index;
}
langstring* LangStringList::sort_index(int *num)
{
if (!num) return 0;
m_sortbuf.resize(0);
m_sortbuf.add(m_gr.get(), m_gr.getlen());
*num = m_sortbuf.getlen() / sizeof(struct langstring);
qsort(m_sortbuf.get(), *num, sizeof(struct langstring), compare_index);
return (struct langstring*) m_sortbuf.get();
}
int LangStringList::compare_uindex(const void *item1, const void *item2)
{
struct langstring *ls1 = (struct langstring *)item1;
struct langstring *ls2 = (struct langstring *)item2;
return ls1->uindex - ls2->uindex;
}
langstring* LangStringList::sort_uindex(int *num)
{
if (!num) return 0;
m_sortbuf.resize(0);
m_sortbuf.add(m_gr.get(), m_gr.getlen());
*num = m_sortbuf.getlen() / sizeof(struct langstring);
qsort(m_sortbuf.get(), *num, sizeof(struct langstring), compare_uindex);
return (struct langstring*) m_sortbuf.get();
}
// ============
// StringsArray
// ============
StringsArray::StringsArray()
{
// We make zero an invalid index. Using 0 will get back an empty string.
m_offsets.set_zeroing(1);
m_strings.add(_T(""), sizeof(_T("")));
}
void StringsArray::resize(int num)
{
m_offsets.resize(num * sizeof(int));
}
int StringsArray::set(int idx, const TCHAR *str)
{
if (idx < 0)
return 0;
if (idx >= (int)(m_offsets.getlen() / sizeof(int)))
resize(idx+1);
int old = ((int*) m_offsets.get())[idx];
// Need to store the TCHAR index so we divide the return value of add by sizeof(TCHAR).
((int*)m_offsets.get())[idx] = m_strings.add(str, (_tcslen(str)+1)*sizeof(TCHAR))/sizeof(TCHAR);
return old;
}
const TCHAR* StringsArray::get(int idx)
{
if (idx < 0 || (unsigned int)idx >= (m_offsets.getlen() / sizeof(int)))
return 0;
return (const TCHAR *) m_strings.get() + ((int*) m_offsets.get())[idx];
}
// =========
// CEXEBuild
// =========
void CEXEBuild::InitLangTables() {
keep_ref = false;
for (int i = 0; i < NLF_STRINGS; i++) {
NLFRefs[i].iRef = 0;
NLFRefs[i].iUnRef = 0;
#ifdef NSIS_CONFIG_LOG
if (i == NLF_NAME) {
NLFRefs[i].iRef++;
NLFRefs[i].iUnRef++;
}
#endif
if (NLFStrings[i].eStaticID & INSTALL_STATIC) {
set_uninstall_mode(0);
DefineLangString(NLFStrings[i].szLangStringName);
}
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (NLFStrings[i].eStaticID & UNINSTALL_STATIC) {
set_uninstall_mode(1);
DefineLangString(NLFStrings[i].szLangStringName);
}
#endif
}
set_uninstall_mode(0);
keep_ref = true;
}
//////////////////////////////////////////////////////////////////////////////
// class CEXEBuild
//
// Note: The functions below refer to the methods related to Languages.
//////////////////////////////////////////////////////////////////////////////
LanguageTable* CEXEBuild::GetLangTable(LANGID &lang, bool create/*=true*/) {
int nlt = lang_tables.getlen() / sizeof(LanguageTable);
LanguageTable *nla = (LanguageTable*)lang_tables.get();
lang = lang ? lang : last_used_lang;
LanguageTable *table = NULL;
for (int i = 0; i < nlt; i++) {
if (lang == nla[i].lang_id) {
table = &nla[i];
break;
}
}
if (!table && create) {
LanguageTable newtable;
newtable.lang_id = lang;
newtable.dlg_offset = 0;
memset(&newtable.nlf, 0, sizeof(NLF));
newtable.lang_strings = new StringsArray;
lang_tables.add(&newtable, sizeof(LanguageTable));
table = (LanguageTable*)lang_tables.get() + nlt;
}
if (table) // update last used language if a table was loaded
last_used_lang = lang;
return table;
}
const TCHAR *CEXEBuild::GetLangNameAndCP(LANGID lang, unsigned int *codepage/*=NULL*/) {
LanguageTable *table = GetLangTable(lang, false);
if (table && table->nlf.m_bLoaded) {
if (codepage)
*codepage = table->nlf.m_uCodePage;
return table->nlf.m_szName;
}
else {
// If the language table does not exist, then we default to Unicode or ANSI
// depending on the target installer type
if (codepage)
*codepage = build_unicode ? 1200 : 1252; // Unicode or CP1252
if (lang == 1033)
return _T("English");
else
return _T("???");
}
}
const TCHAR *CEXEBuild::GetLangNameAndCPForVersionResource(LANGID &lang, unsigned int *codepage/*=NULL*/, bool deflangfallback/*=true*/) {
const TCHAR *langname = GetLangNameAndCP(lang, codepage);
if (0 == lang) {
if (deflangfallback)
lang = last_used_lang;
else
langname = _T("Neutral");
}
return langname;
}
int CEXEBuild::DefineLangString(const TCHAR *name, int process/*=-1*/) {
int index, uindex, pos, ret, sn;
/* If not exist, index and uindex will get -1. */
pos = build_langstrings.get(name, &sn, &index, &uindex);
if (pos < 0) {
pos = build_langstrings.add(name);
}
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (!this->uninstall_mode) {
#endif
if (index < 0) {
// Did not exist. Increment.
index = this->build_langstring_num++;
}
ret = -index - 1;
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
}
else {
if (uindex < 0) {
// Did not exist. Increment.
uindex = this->ubuild_langstring_num++;
}
ret = -uindex - 1;
}
#endif
// Now set the new index and uindex values with the
// passed in proces value.
build_langstrings.set(pos, index, uindex, process);
// set reference count for NLF strings
if (this->keep_ref && name[0] == _T('^')) {
for (int i = 0; i < NLF_STRINGS; i++) {
if (!_tcscmp(name, NLFStrings[i].szLangStringName)) {
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (this->uninstall_mode)
this->NLFRefs[i].iUnRef++;
else
#endif
this->NLFRefs[i].iRef++;
break;
}
}
}
return ret;
}
int CEXEBuild::DefineInnerLangString(int id, int process/*=-1*/) {
bool old_keep_ref = keep_ref;
// set reference count for NLF strings
if (keep_ref) {
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (uninstall_mode)
NLFRefs[id].iUnRef++;
else
#endif
NLFRefs[id].iRef++;
keep_ref = false;
}
int ret = DefineLangString(NLFStrings[id].szLangStringName, process);
keep_ref = old_keep_ref;
return ret;
}
// A LangString is a string variable that varies in value depending on what
// language is being used. This function sets the string value for the
// variable 'name' for a given language ID.
//
// @return If the language id, the variable name or string is invalid, it will
// return a PS_ERROR. If this function call is overwriting a set user string,
// this will return a PS_WARNING.
int CEXEBuild::SetLangString(TCHAR *name, LANGID lang, const TCHAR *str, BOOL unicode) {
if (!str || !name) return PS_ERROR;
LanguageTable *table = GetLangTable(lang);
if (!table) return PS_ERROR;
int sn;
if (_tcsclen(str) > NSIS_MAX_STRLEN-1)
warning_fl(_T("LangString \"%s\" longer than NSIS_MAX_STRLEN!"), name);
int pos = build_langstrings.get(name, &sn);
if (pos < 0)
pos = build_langstrings.add(name, &sn);
if (table->lang_strings->set(sn, unicode ? str : CtoTString2(TtoCString(str),table->nlf.m_uCodePage)))
return PS_WARNING;
return PS_OK;
}
#ifndef _UNICODE
int CEXEBuild::SetUTF8LangString(TCHAR *name, LANGID lang, const char* stru8)
{
LanguageTable *table = GetLangTable(lang);
if (!table) return PS_ERROR;
if (!Platform_SupportsUTF8Conversion()) return PS_ERROR;
EXEHEADTCHAR_T *bufEHTStr = UTF8ToExeHeadTStrDup(stru8, table->nlf.m_uCodePage);
if (!bufEHTStr) return PS_ERROR;
const int ret = SetLangString(name, lang, bufEHTStr, sizeof(EXEHEADTCHAR_T) > 1);
free(bufEHTStr);
return ret;
}
#endif
// Sets the user string to the specific NLF_STRINGS id.
//
// @return If the id is invalid or the string is not valid, it will return a
// PS_ERROR. If this function call is overwriting a set user string, this
// will return a PS_WARNING.
int CEXEBuild::SetInnerString(int id, TCHAR *str) {
if ((unsigned int)id >= NLF_STRINGS || !str) return PS_ERROR;
int ret = PS_OK;
const TCHAR *ps = UserInnerStrings.get(id);
if (ps && *ps)
ret = PS_WARNING;
UserInnerStrings.set(id, str);
return ret;
}
int CEXEBuild::GenerateLangTable(LanguageTable *lt, int num_lang_tables) {
// Add all installer language strings
int i, j, l, tabsset;
struct langstring* lang_strings = NULL;
TinyGrowBuf *string_ptrs = new TinyGrowBuf[num_lang_tables];
tabsset = 1;
while (tabsset)
{
tabsset = 0;
for (i = num_lang_tables; i--; )
{
// Fill in default values for all used language strings that we can
FillLanguageTable(&lt[i]);
// Make sure the string lists are large enough
string_ptrs[i].set_zeroing(1);
if (!uninstall_mode)
string_ptrs[i].resize(build_langstring_num * sizeof(int));
else
string_ptrs[i].resize(ubuild_langstring_num * sizeof(int));
}
// For all current language strings
if (!uninstall_mode)
lang_strings = build_langstrings.sort_index(&l);
else
lang_strings = build_langstrings.sort_uindex(&l);
for (j = 0; j < l; j++)
{
int lang_string_index;
if (!uninstall_mode)
lang_string_index = lang_strings[j].index;
else
lang_string_index = lang_strings[j].uindex;
// Is this language string used (in the installer)?
if (lang_string_index >= 0)
{
// For each language
for (i = num_lang_tables; i--; )
{
// Get the current string pointer
int *ptr = (int *)string_ptrs[i].get() + lang_string_index;
// Not already set?
if (!*ptr)
{
// Get the language string and its name
const TCHAR *str = lt[i].lang_strings->get(lang_strings[j].sn);
const TCHAR *lsn = build_langstrings.offset2name(lang_strings[j].name);
// lsn = variable name, str = value
if (!str || !*str)
{
// No string is defined; give a warning (for user strings only)
if (lsn[0] != _T('^'))
{
if (lt[i].nlf.m_bLoaded)
warning(_T("LangString \"%s\" is not set in language table of language %s"), lsn, lt[i].nlf.m_szName);
else
warning(_T("LangString \"%s\" is not set in language table of language %d"), lsn, lt[i].lang_id);
}
}
else
{
// Add the language string to the string data block
TCHAR fn[1024];
_stprintf(fn, _T("LangString %s"), lsn);
curfilename = fn;
linecnt = lt[i].lang_id;
*ptr = add_string(str, lang_strings[j].process, (WORD) lt[i].nlf.m_uCodePage);
curfilename = 0;
// Indicate that we should check again for any newly referenced language strings
tabsset++;
}
}
}
}
}
}
// Optimize langstrings and check for recursion
for (i = num_lang_tables; i--; )
{
TinyGrowBuf rec;
int *lst = (int *)string_ptrs[i].get();
int langstring_num;
if (!uninstall_mode)
langstring_num = build_langstring_num;
else
langstring_num = ubuild_langstring_num;
for (j = 0; j < langstring_num; j++)
{
// Does this string reference another language string directly?
while (lst[j] < 0)
{
// Search through list of language string references
for (l = 0; (unsigned int)l < rec.getlen() / sizeof(int); l++)
{
if (((int*)rec.get())[l] == lst[j])
{
// We have the index of a recursive language string; now find the name
const TCHAR *name = _T("(unnamed)");
for (l = 0; l < langstring_num; l++)
{
if (lang_strings[l].index == j)
{
name = build_langstrings.offset2name(lang_strings[l].name);
}
}
ERROR_MSG(_T("Error: LangString %s is recursive!\n"), name);
delete [] string_ptrs;
return PS_ERROR;
}
}
// Add this reference to the list
rec.add(&lst[j], sizeof(int));
// and dereference it
lst[j] = lst[-lst[j] - 1];
}
rec.resize(0);
}
}
// Add language tables into their datablock
for (i = num_lang_tables; i--; )
{
cur_langtables->add(&lt[i].lang_id, sizeof(LANGID));
cur_langtables->add(&lt[i].dlg_offset, sizeof(int));
int rtl = lt[i].nlf.m_bRTL ? 1 : 0;
cur_langtables->add(&rtl, sizeof(int));
cur_langtables->add(string_ptrs[i].get(), string_ptrs[i].getlen());
string_ptrs[i].resize(0);
}
cur_header->blocks[NB_LANGTABLES].num = num_lang_tables;
cur_header->langtable_size = cur_langtables->getlen() / num_lang_tables;
delete [] string_ptrs;
return PS_OK;
}
int CEXEBuild::GenerateLangTables() {
int i;
LanguageTable *lt = (LanguageTable*)lang_tables.get();
SCRIPT_MSG(_T("Generating language tables... "));
if (
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
ubuild_langstring_num > MAX_CODED ||
#endif
build_langstring_num > MAX_CODED
)
{
ERROR_MSG(_T("\nError: too many LangStrings. Maximum allowed is %u.\n"), MAX_CODED);
return PS_ERROR;
}
// If we have no tables (user didn't set any string and didn't load any NLF) create the default one
if (!lang_tables.getlen()) {
LANGID lang = NSIS_DEFAULT_LANG;
LanguageTable *table = GetLangTable(lang);
if (!table) return PS_ERROR;
lt = (LanguageTable*)lang_tables.get();
}
// Apply default font
if (*build_font)
{
try {
init_res_editor();
#define ADD_FONT(id) { \
BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \
if (dlg) { \
CDialogTemplate td(dlg,build_unicode); \
res_editor->FreeResource(dlg); \
td.SetFont(build_font, (WORD) build_font_size); \
DWORD dwSize; \
dlg = td.Save(dwSize); \
res_editor->UpdateResource(RT_DIALOG, id, NSIS_DEFAULT_LANG, dlg, dwSize); \
delete [] dlg; \
} \
}
#ifdef NSIS_CONFIG_LICENSEPAGE
ADD_FONT(IDD_LICENSE);
ADD_FONT(IDD_LICENSE_FSRB);
ADD_FONT(IDD_LICENSE_FSCB);
#endif
ADD_FONT(IDD_DIR);
#ifdef NSIS_CONFIG_COMPONENTPAGE
ADD_FONT(IDD_SELCOM);
#endif
ADD_FONT(IDD_INST);
ADD_FONT(IDD_INSTFILES);
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
ADD_FONT(IDD_UNINST);
#endif
#ifdef NSIS_CONFIG_CRC_SUPPORT
ADD_FONT(IDD_VERIFY);
#endif
#undef ADD_FONT
}
catch (exception& err) {
ERROR_MSG(_T("\nError while applying font: %s\n"), CtoTStrParam(err.what()));
return PS_ERROR;
}
}
// Fill tables with defaults (if needed) and with instruction strings
// Create language specific resources (currently only dialogs with different fonts)
int num_lang_tables = lang_tables.getlen() / sizeof(LanguageTable);
// if there is one string table then there is no need for two sets of dialogs
int cur_offset = num_lang_tables == 1 ? 0 : 100;
for (i = 0; i < num_lang_tables; i++)
{
if ((lt[i].nlf.m_szFont && !*build_font) || lt[i].nlf.m_bRTL)
{
lt[i].dlg_offset = cur_offset;
TCHAR *font = lt[i].nlf.m_szFont;
if (*build_font) font = 0;
try {
init_res_editor();
#define ADD_FONT(id) { \
BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \
if (dlg) { \
CDialogTemplate td(dlg,build_unicode,lt[i].nlf.m_uCodePage); \
res_editor->FreeResource(dlg); \
if (font) td.SetFont(font, (WORD) lt[i].nlf.m_iFontSize); \
if (lt[i].nlf.m_bRTL) { \
td.ConvertToRTL(); \
DialogItemTemplate* dir = td.GetItem(IDC_DIR); \
if (id == IDD_DIR && dir) { \
if ((dir->dwStyle & ES_CENTER) == 0) dir->dwStyle ^= ES_RIGHT; \
dir->dwExtStyle &= ~(WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR); \
} \
} \
DWORD dwSize; \
dlg = td.Save(dwSize); \
res_editor->UpdateResource(RT_DIALOG, id+cur_offset, NSIS_DEFAULT_LANG, dlg, dwSize); \
delete [] dlg; \
} \
}
#ifdef NSIS_CONFIG_LICENSEPAGE
ADD_FONT(IDD_LICENSE);
ADD_FONT(IDD_LICENSE_FSRB);
ADD_FONT(IDD_LICENSE_FSCB);
#endif
ADD_FONT(IDD_DIR);
#ifdef NSIS_CONFIG_COMPONENTPAGE
ADD_FONT(IDD_SELCOM);
#endif
ADD_FONT(IDD_INST);
ADD_FONT(IDD_INSTFILES);
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
ADD_FONT(IDD_UNINST);
#endif
#ifdef NSIS_CONFIG_CRC_SUPPORT
ADD_FONT(IDD_VERIFY);
#endif
#undef ADD_FONT
}
catch (exception& err) {
ERROR_MSG(_T("\nError while applying NLF font/RTL: %s\n"), CtoTStrParam(err.what()));
return PS_ERROR;
}
cur_offset += 100;
}
}
int orig_uninstall_mode = uninstall_mode;
set_uninstall_mode(0);
if (GenerateLangTable(lt, num_lang_tables) != PS_OK)
return PS_ERROR;
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
set_uninstall_mode(1);
if (GenerateLangTable(lt, num_lang_tables) != PS_OK)
return PS_ERROR;
#endif
set_uninstall_mode(orig_uninstall_mode);
SCRIPT_MSG(_T("Done!\n"));
return PS_OK;
}
void CEXEBuild::FillLanguageTable(LanguageTable *table) {
for (int i = 0; i < NLF_STRINGS; i++) {
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (!NLFRefs[i].iUnRef && !NLFRefs[i].iRef)
continue;
#else
if (!NLFRefs[i].iRef)
continue;
#endif
else if (i == NLF_SPACE_REQ || i == NLF_SPACE_AVAIL)
{
if (no_space_texts)
{
continue;
}
}
int sn, index;
int pos = build_langstrings.get(NLFStrings[i].szLangStringName, &sn, &index);
if (pos >= 0) {
const TCHAR *str = table->lang_strings->get(sn);
if (!str || !*str) {
const TCHAR *us = UserInnerStrings.get(i);
if (i == NLF_NAME_DA && (!us || !*us))
{
// if the user didn't set NLF_NAME_DA we set it to $(^Name)
table->lang_strings->set(sn, _T("$(^Name)"));
}
if (us && *us) {
table->lang_strings->set(sn, (TCHAR *) us);
}
else {
const TCHAR *dstr = table->nlf.m_szStrings[i] ? table->nlf.m_szStrings[i] : NLFStrings[i].szDefault;
if (!dstr)
continue;
if (i == NLF_BRANDING) {
TCHAR temp[NSIS_MAX_STRLEN + sizeof(NSIS_VERSION)];
_stprintf(temp, dstr, NSIS_VERSION);
table->lang_strings->set(sn, temp);
continue;
}
else if (i == NLF_FONT)
{
TCHAR *font = *build_font ? build_font : table->nlf.m_szFont;
if (font)
table->lang_strings->set(sn, font);
else
table->lang_strings->set(sn, dstr);
continue;
}
else if (i == NLF_FONTSIZE)
{
WORD font_size = *build_font ? (WORD) build_font_size : (WORD) table->nlf.m_iFontSize;
if (font_size)
{
TCHAR temp[64];
_stprintf(temp, _T("%d"), font_size);
table->lang_strings->set(sn, temp);
}
else
table->lang_strings->set(sn, dstr);
continue;
}
table->lang_strings->set(sn, dstr);
}
}
}
}
}
TCHAR SkipComments(FILE *f) {
int c;
while ((c = _fgettc(f))) {
while (c == _T('\n') || c == _T('\r')) {
c = _fgettc(f); // Skip empty lines
}
if (c == _T('#') || c == _T(';')) {
while ((c = _fgettc(f))) {
if (c == _T('\n')) break;
}
}
else break;
}
return (TCHAR) c;
}
// NSIS Language File parser
LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
BOOL unicode;
FILE *f = FOPENTEXT2(filename, "r", &unicode);
if (!f) {
ERROR_MSG(_T("Error: Can't open language file - \"%s\"!\n"),filename);
return 0;
}
#ifndef _UNICODE
char fencoding = 0; // 0 = ansi, 8 = utf-8 (16/17 for uft-16le/be not supported)
if (IsUTF8BOM(f)) fencoding = 8;
#endif
// Check header
TCHAR buf[NSIS_MAX_STRLEN];
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (_tcsncmp(buf, _T("NLF v"), 5)) {
ERROR_MSG(_T("Error: Invalid language file.\n"));
return 0;
}
int nlf_version = _ttoi(buf+5);
if (nlf_version != NLF_VERSION) {
if (nlf_version != 2 && nlf_version != 3 && nlf_version != 4 && nlf_version != 5) {
ERROR_MSG(_T("Error: Language file version doesn't match NSIS version.\n"));
return 0;
}
}
// Get language ID
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
LANGID lang_id = _ttoi(buf);
// Get appropriate table
LanguageTable *table = GetLangTable(lang_id);
if (!table)
return 0;
NLF *nlf = &table->nlf;
if (nlf->m_bLoaded) {
ERROR_MSG(_T("Error: can't load same language file twice.\n"));
return 0;
}
// Generate language name
TCHAR *p, *p2, t = 0;
p = _tcsrchr(filename, _T('.'));
if (p) {
t = *p;
*p = 0;
}
p2 = _tcsrchr(filename, _T('\\'));
if (p2) {
p2++;
nlf->m_szName = (TCHAR*)malloc((_tcslen(p2)+1)*sizeof(TCHAR));
_tcscpy(nlf->m_szName, p2);
}
else {
nlf->m_szName = (TCHAR*)malloc((_tcslen(filename)+1)*sizeof(TCHAR));
_tcscpy(nlf->m_szName, filename);
}
if (p) *p = t;
if (nlf_version != NLF_VERSION) {
warning_fl(_T("%s language file version doesn't match. Using default English texts for missing strings."), nlf->m_szName);
}
// set ^Language
nlf->m_szStrings[NLF_LANGUAGE] = _tcsdup(nlf->m_szName);
int temp;
// Get font
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (!nlf->m_szFont) {
temp=_tcslen(buf);
while (buf[temp-1] == _T('\n') || buf[temp-1] == _T('\r')) {
buf[temp-1] = 0;
temp--;
}
if (buf[0] != _T('-') || buf[1] != 0) {
nlf->m_szFont = (TCHAR*)malloc((_tcslen(buf)+1)*sizeof(TCHAR));
_tcscpy(nlf->m_szFont, buf);
}
}
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (!nlf->m_iFontSize) {
if (buf[0] != _T('-') || buf[1] != 0) {
nlf->m_iFontSize = _ttoi(buf);
}
}
// Get code page
nlf->m_uCodePage = CP_ACP;
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (buf[0] != _T('-') || buf[1] != 0) {
nlf->m_uCodePage = _ttoi(buf);
if (!IsValidCodePage(nlf->m_uCodePage))
nlf->m_uCodePage = CP_ACP;
}
#ifdef _UNICODE
if (!unicode && nlf->m_szFont) // convert font name from ANSI to Unicode now that we know the language codepage
{
TCHAR* str = nlf->m_szFont;
nlf->m_szFont = _tcsdup(CtoTString2(TtoCString(str), table->nlf.m_uCodePage));
free(str);
}
#else
if (8 == fencoding && nlf->m_szFont)
{
TCHAR* str = nlf->m_szFont;
EXEHEADTCHAR_T *bufConv = UTF8ToExeHeadTStrDup(str, table->nlf.m_uCodePage);
nlf->m_szFont = bufConv;
if (!nlf->m_szFont)
{
ERROR_MSG(_T("Error: Unable to convert font name\n"));
return 0;
}
free(str);
}
#endif
// Get RTL setting
nlf->m_szStrings[NLF_RTL] = (TCHAR *)malloc(2*sizeof(TCHAR));
nlf->m_bRTL = false;
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (buf[0] == _T('R') && buf[1] == _T('T') && buf[2] == _T('L') && (!buf[3] || buf[3] == _T('\r') || buf[3] == _T('\n'))) {
nlf->m_bRTL = true;
_tcscpy(nlf->m_szStrings[NLF_RTL], _T("1"));
}
else {
_tcscpy(nlf->m_szStrings[NLF_RTL], _T("0"));
}
// Read strings
for (int i = 0; i < NLF_STRINGS_NO_SPECIAL; i++) {
// skip virtual strings
if (!NLFStrings[i].szDefault)
continue;
// Fill in for missing strings
// 0 will mean default will be used from NLFStrings
switch (i) {
case NLF_BTN_LICENSE_AGREE:
case NLF_BTN_LICENSE_DISAGREE:
if (nlf_version >= 3) break;
case NLF_LOG_INSTALL_PROCESS:
case NLF_BYTE:
case NLF_KILO:
case NLF_MEGA:
case NLF_GIGA:
case NLF_REGISTERING:
case NLF_UNREGISTERING:
if (nlf_version >= 4) break;
case NLF_FILE_ERROR_NOIGNORE:
if (nlf_version >= 5) break;
case NLF_USUBCAPTION_OPTIONS:
case NLF_USUBCAPTION_DIR:
case NLF_CLICK_NEXT:
case NLF_CLICK_INSTALL:
case NLF_CLICK_UNINSTALL:
case NLF_LICENSE_TEXT:
case NLF_LICENSE_TEXT_FSCB:
case NLF_LICENSE_TEXT_FSRB:
case NLF_ULICENSE_TEXT:
case NLF_ULICENSE_TEXT_FSCB:
case NLF_ULICENSE_TEXT_FSRB:
case NLF_COMP_TEXT:
case NLF_UCOMP_TEXT:
case NLF_UCOMP_SUBTEXT1:
case NLF_UCOMP_SUBTEXT1_NO_INST_TYPES:
case NLF_UCOMP_SUBTEXT2:
case NLF_DIR_TEXT:
case NLF_DIR_BROWSETEXT:
case NLF_UDIR_TEXT:
case NLF_UDIR_SUBTEXT:
case NLF_UDIR_BROWSETEXT:
case NLF_UNINST_TEXT:
if (nlf_version >= 6) break;
nlf->m_szStrings[i] = 0;
continue;
}
buf[0] = SkipComments(f);
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
#ifndef _UNICODE
if (8 == fencoding)
{
if (!Platform_SupportsUTF8Conversion()) {
ERROR_MSG(_T("Error: UTF-8 language files not supported on this OS!\n"));
return 0;
}
EXEHEADTCHAR_T *bufConv = UTF8ToExeHeadTStrDup(buf, nlf->m_uCodePage);
if (!bufConv) {
ERROR_MSG(_T("Error: Invalid UTF-8? (string #%d - \"%s\")\n"), i, NLFStrings[i].szLangStringName);
return 0;
}
else {
UINT cch = _tcslen(bufConv);
_tcsnccpy(buf, bufConv, NSIS_MAX_STRLEN);
if (cch >= NSIS_MAX_STRLEN-1) {
buf[NSIS_MAX_STRLEN-1] = _T('\0'); // Make sure we fail the "String too long" check
}
}
free(bufConv);
}
#endif
if (_tcslen(buf) == NSIS_MAX_STRLEN-1) {
ERROR_MSG(_T("Error: String too long (string #%d - \"%s\")\n"), i, NLFStrings[i].szLangStringName);
return 0;
}
temp=_tcslen(buf);
while (buf[temp-1] == _T('\n') || buf[temp-1] == _T('\r')) {
buf[--temp] = 0;
}
TCHAR *in = buf;
// trim quotes
if (buf[0] == _T('"') && buf[temp-1] == _T('"')) {
in++;
buf[--temp] = 0;
}
nlf->m_szStrings[i] = (TCHAR*)malloc((temp+1)*sizeof(TCHAR));
TCHAR *out;
for (out = nlf->m_szStrings[i]; *in; in++, out++) {
if (*in == _T('\\')) {
in++;
switch (*in) {
case _T('n'):
*out = _T('\n');
break;
case _T('r'):
*out = _T('\r');
break;
case _T('t'):
*out = _T('\t');
break;
default:
*out++ = _T('\\');
*out = *in;
}
}
else *out = *in;
}
*out = 0;
#ifdef _UNICODE
if (!unicode)
{
TCHAR* str = nlf->m_szStrings[i];
nlf->m_szStrings[i] = _tcsdup(CtoTString2(TtoCString(str),table->nlf.m_uCodePage));
free(str);
}
#endif
}
fclose(f);
nlf->m_bLoaded = true;
return table;
}
void CEXEBuild::DeleteLangTable(LanguageTable *table) {
if (table->nlf.m_szName)
free(table->nlf.m_szName);
if (table->nlf.m_szFont)
free(table->nlf.m_szFont);
delete table->lang_strings;
for (int i = 0; i < NLF_STRINGS; i++) {
if (table->nlf.m_szStrings[i])
free(table->nlf.m_szStrings[i]);
}
}