Unicode port: Unicode version of NSIS can now generate both ANSI & Unicode installers (using new instruction UnicodeInstaller on/off).

Stubs & Plugins differentiation is done automatically using a 'W' suffix.
SConscripts need to be reviewed to generate both variants of Plugins & pluginapi.lib under Unicode compilation.

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6100 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
wizou 2010-06-14 10:07:22 +00:00
parent fbc7cb8fd0
commit 57f7ff8a1c
24 changed files with 376 additions and 112 deletions

View file

@ -24,19 +24,27 @@ example = Split("""
extdll.inc
""")
Import('env plugin_env')
Import('env plugin_env plugin_uenv')
# build library
api_env = env.Clone()
api_env.Append(CPPPATH = ['#Source/exehead'])
lib = api_env.Library(lib_target, lib_files)
# in Unicode compilation, we should really build both variants but I can't get it to work [Wizou]
if env['UNICODE']:
api_uenv = plugin_uenv.Clone()
api_uenv.Append(CPPPATH = ['#Source/exehead'])
libW = api_uenv.Library(lib_target+'W', lib_files)
lib = libW
else:
api_env = plugin_env.Clone()
api_env.Append(CPPPATH = ['#Source/exehead'])
lib = api_env.Library(lib_target, lib_files)
# distribute library, files and examples
env.DistributeExamples(api_files, path='Plugin/nsis')
if env['PLATFORM'] == 'win32':
if env['UNICODE']:
env.DistributeExamples(libW, lib_target+".lib", path='Plugin/nsis')
env.DistributeExamples(lib, path='Plugin/nsis')
else:
@ -57,3 +65,6 @@ env.Install('#$BUILD_PREFIX/api/nsis', api_files + lib)
plugin_env.Append(CPPPATH = ['#$BUILD_PREFIX/api'])
plugin_env.Append(LIBPATH = ['#$BUILD_PREFIX/api/nsis'])
plugin_env.Append(LIBS = [lib_target])
plugin_uenv.Append(CPPPATH = ['#$BUILD_PREFIX/api'])
plugin_uenv.Append(LIBPATH = ['#$BUILD_PREFIX/api/nsis'])
plugin_uenv.Append(LIBS = [lib_target+'W'])

View file

@ -126,7 +126,7 @@ Macros and conversion functions for InstallOptions
InitPluginsDir
File "/oname=$PLUGINSDIR\${FILE}" "${FILE}"
!ifdef NSIS_UNICODE
InstallOptions::make_unicode "$PLUGINSDIR\${FILE}"
InstallOptionsW::make_unicode "$PLUGINSDIR\${FILE}"
!endif
!insertmacro INSTALLOPTIONS_WRITE "${FILE}" "Settings" "RTL" "$(^RTL)"
@ -139,7 +139,7 @@ Macros and conversion functions for InstallOptions
InitPluginsDir
File "/oname=$PLUGINSDIR\${FILENAME}" "${FILE}"
!ifdef NSIS_UNICODE
InstallOptions::make_unicode "$PLUGINSDIR\${FILENAME}"
InstallOptionsW::make_unicode "$PLUGINSDIR\${FILENAME}"
!endif
!insertmacro INSTALLOPTIONS_WRITE "${FILENAME}" "Settings" "RTL" "$(^RTL)"

View file

@ -706,7 +706,9 @@ BOOL CALLBACK DialogProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
}
DWORD WINAPI MakeNSISProc(LPVOID p) {
#ifdef _UNICODE
TCHAR buf[1024];
#endif
char iobuf[1024]; //i/o buffer
STARTUPINFO si={sizeof(si),};
SECURITY_ATTRIBUTES sa={sizeof(sa),};

View file

@ -53,4 +53,4 @@ test_env.Append(CPPPATH = ['#$BUILD_CONFIG'])
# return
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env')
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_env plugin_env')

View file

@ -309,4 +309,4 @@ if makensis_env['PLATFORM'] == 'hpux':
### return
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env')
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_env plugin_env')

View file

@ -125,4 +125,4 @@ test_env.Append(CPPPATH = ['#$BUILD_CONFIG'])
# return
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env')
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_env plugin_env')

View file

@ -41,8 +41,9 @@ if defenv['DEBUG']:
defenv.Append(LINKFLAGS = ['/debug'])
### unicode
if defenv['UNICODE']:
defenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE'])
tdefenv = defenv.Clone()
if tdefenv['UNICODE']:
tdefenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE'])
### workarounds
@ -101,19 +102,19 @@ stub_env.Append(CCFLAGS = ['/W3']) # level 3 warnings
stub_env.Append(LINKFLAGS = ['/opt:nowin98']) # 512 bytes align
if defenv['UNICODE']:
stub_env.Append(LINKFLAGS = ['/entry:wWinMain']) # Unicode entry point
else:
stub_env.Append(LINKFLAGS = ['/entry:WinMain']) # ANSI entry point
stub_env.Append(LINKFLAGS = ['$NODEFLIBS_FLAG']) # no default libraries
stub_env.Append(LINKFLAGS = ['$MAP_FLAG']) # generate map file
stub_env.Append(CCFLAGS = ['/FAcs']) # full listing files
stub_env.Append(CCFLAGS = ['/Fa${TARGET}.lst']) # listing file name
stub_uenv = stub_env.Clone()
stub_uenv.Append(LINKFLAGS = ['/entry:wWinMain']) # Unicode entry point
stub_uenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE'])
stub_env.Append(LINKFLAGS = ['/entry:WinMain']) # ANSI entry point
### makensis environment
makensis_env = defenv.Clone()
makensis_env = tdefenv.Clone()
makensis_env.Append(CPPPATH = ['#$BUILD_CONFIG'])
@ -140,9 +141,12 @@ plugin_env.Append(CCFLAGS = ['/W3']) # level 3 warnings
plugin_env.Append(LINKFLAGS = ['/opt:nowin98']) # 512 bytes align
plugin_env.Append(LINKFLAGS = ['$MAP_FLAG']) # generate map file
plugin_uenv = plugin_env.Clone()
plugin_uenv.Append(CPPDEFINES = ['_UNICODE', 'UNICODE'])
### util environment
util_env = defenv.Clone()
util_env = tdefenv.Clone()
if not defenv['DEBUG']:
util_env.Append(CCFLAGS = ['/O1']) # optimize for speed
@ -209,8 +213,10 @@ def add_file_to_emitter(env, emitter_name, file):
def add_file(file):
file = File(file)
add_file_to_emitter(stub_env, 'PROGEMITTER', file)
add_file_to_emitter(stub_uenv, 'PROGEMITTER', file)
add_file_to_emitter(util_env, 'PROGEMITTER', file)
add_file_to_emitter(plugin_env, 'SHLIBEMITTER', file)
add_file_to_emitter(plugin_uenv, 'SHLIBEMITTER', file)
#
# MSVC 6 SP6 doesn't like direct shifting of 64-bit integers.
@ -240,6 +246,7 @@ else:
if not conf.TryLink(int64test, '.c'):
stub_env.Append(CPPDEFINES = ['_NSIS_NO_INT64_SHR'])
stub_uenv.Append(CPPDEFINES = ['_NSIS_NO_INT64_SHR'])
conf.Finish()
@ -256,4 +263,4 @@ conf.Finish()
### return
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env')
Return('stub_env makensis_env plugin_env util_env cp_util_env test_env stub_uenv plugin_uenv')

View file

@ -420,8 +420,10 @@ plugin_env = envs[2]
util_env = envs[3]
cp_util_env = envs[4]
test_env = envs[5]
stub_uenv = envs[6]
plugin_uenv = envs[7]
Export('stub_env makensis_env plugin_env util_env cp_util_env test_env')
Export('stub_env makensis_env plugin_env plugin_uenv util_env cp_util_env test_env')
######################################################################
####### Distribution ###
@ -484,12 +486,17 @@ defenv.DistributeConf('nsisconf.nsh')
####### Stubs ###
######################################################################
def BuildStub(compression, solid):
env = stub_env.Clone()
def BuildStub(compression, solid, unicode):
suffix = ''
if solid:
suffix = '_solid'
if unicode:
suffix += 'W'
env = stub_uenv.Clone()
else:
env = stub_env.Clone()
build_dir = '$BUILD_PREFIX/stub_%s%s' % (compression, suffix)
@ -507,8 +514,12 @@ for stub in stubs:
if stub in defenv['SKIPSTUBS']:
continue
BuildStub(stub, False)
BuildStub(stub, True)
if defenv['UNICODE']:
BuildStub(stub, False, True)
BuildStub(stub, True, True)
BuildStub(stub, False, False)
BuildStub(stub, True, False)
defenv.DistributeStubs('Source/exehead/uninst.ico',names='uninst')
@ -569,11 +580,25 @@ def DistributeExtras(env, target, examples, docs):
####### Plug-ins ###
######################################################################
def BuildPlugin(target, source, libs, examples = None, docs = None,
def BuildPluginBoth(target, source, libs, examples = None, docs = None,
entry = 'DllMain', res = None, resources = None,
defines = None, flags = None, nodeflib = True,
cppused = False):
env = plugin_env.Clone()
# this function should build the ANSI & Unicode variant of the DLL, but I can't get it to work... help!!! [Wizou]
if defenv['UNICODE']:
#VariantDir('$BUILD_PREFIX/' + plugin + 'W', path)
BuildPlugin(target, source, libs, examples, docs, entry, res, resources, defines, flags, nodeflib, cppused, True)
else:
BuildPlugin(target, source, libs, examples, docs, entry, res, resources, defines, flags, nodeflib, cppused, False)
def BuildPlugin(target, source, libs, examples = None, docs = None,
entry = 'DllMain', res = None, resources = None,
defines = None, flags = None, nodeflib = True,
cppused = False, unicode = False):
if unicode:
env = plugin_uenv.Clone()
else:
env = plugin_env.Clone()
if cppused and env['CPP_REQUIRES_STDLIB']:
nodeflib = False
@ -595,7 +620,10 @@ def BuildPlugin(target, source, libs, examples = None, docs = None,
plugin = i
break
env.DistributePlugin(plugin)
if unicode:
env.DistributePlugin(plugin, str(plugin)[:-4]+"W.dll") # tweak to generate both plugin.dll & pluginW.dll (until we get to build really both variant)
#if not unicode: # distribute extras only for ANSI builds
DistributeExtras(env, target, examples, docs)
for plugin in plugin_libs + plugins:
@ -604,8 +632,10 @@ for plugin in plugin_libs + plugins:
path = 'Contrib/' + plugin
build_dir = '$BUILD_PREFIX/' + plugin
exports = {'BuildPlugin' : BuildPlugin, 'env' : plugin_env.Clone()}
if defenv['UNICODE']:
exports = {'BuildPlugin' : BuildPluginBoth, 'env' : plugin_uenv.Clone()}
else:
exports = {'BuildPlugin' : BuildPlugin, 'env' : plugin_env.Clone()}
defenv.SConscript(dirs = path, build_dir = build_dir, duplicate = False, exports = exports)
######################################################################

View file

@ -91,8 +91,9 @@ void ReadVarLenArr(LPBYTE &seeker, WCHAR* &readInto, unsigned int uCodePage) {
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CDialogTemplate::CDialogTemplate(BYTE* pbData, unsigned int uCodePage) {
CDialogTemplate::CDialogTemplate(BYTE* pbData, bool build_unicode, unsigned int uCodePage) {
m_uCodePage = uCodePage;
m_build_unicode = build_unicode;
m_dwHelpId = 0;
m_szClass = 0;
@ -598,7 +599,7 @@ BYTE* CDialogTemplate::Save(DWORD& dwSize) {
// Write class variant length array
WCHAR *szClass = m_vItems[i]->szClass;
#ifdef _UNICODE
if (!IS_INTRESOURCE(szClass) && !_wcsicmp(szClass, L"RichEdit20A"))
if (!IS_INTRESOURCE(szClass) && m_build_unicode && !_wcsicmp(szClass, L"RichEdit20A"))
szClass = L"RichEdit20W"; // transmute ANSI RichEdit control into Unicode RichEdit
#endif
WriteStringOrId(szClass);

View file

@ -107,7 +107,7 @@ typedef struct {
class CDialogTemplate {
public:
CDialogTemplate(BYTE* pbData, unsigned int uCodePage=CP_ACP);
CDialogTemplate(BYTE* pbData, bool build_unicode, unsigned int uCodePage=CP_ACP);
virtual ~CDialogTemplate();
short GetWidth();
@ -136,6 +136,7 @@ public:
private:
bool m_bExtended;
bool m_build_unicode;
DWORD m_dwHelpId; // Extended only

View file

@ -47,7 +47,7 @@ void Plugins::FindCommands(const tstring &path, bool displayInfo)
for (dir_reader::iterator files_itr = dr->files().begin();
files_itr != dr->files().end();
files_itr++)
files_itr++) // note: files are listed alphabetically, so plugin.dll will be listed before pluginW.dll
{
if (!dir_reader::matches(*files_itr, _T("*.dll")))
continue;
@ -109,7 +109,10 @@ void Plugins::GetExports(const tstring &pathToDll, bool displayInfo)
return;
}
const tstring dllName = remove_file_extension(get_file_name(pathToDll));
tstring dllName = remove_file_extension(get_file_name(pathToDll));
#ifdef _UNICODE
bool unicodeDll = dllName[dllName.size()-1] == 'W';
#endif
FIX_ENDIAN_INT16_INPLACE(NTHeaders->FileHeader.Characteristics);
if (NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL)
@ -143,6 +146,12 @@ void Plugins::GetExports(const tstring &pathToDll, bool displayInfo)
const tstring lcsig = lowercase(signature);
m_command_to_path[lcsig] = pathToDll;
m_command_lowercase_to_command[lcsig] = signature;
#ifdef _UNICODE
const tstring lcsigA = lowercase(dllName.substr(0,dllName.size()-1) + _T("::") + tstring(CtoTString(name)));
if (unicodeDll && m_command_to_path.find(lcsigA) != m_command_to_path.end())
m_unicode_variant[lcsigA] = signature;
else
#endif
if (displayInfo)
_ftprintf(g_output, _T(" - %s\n"), signature.c_str());
}
@ -180,6 +189,10 @@ tstring Plugins::NormalizedCommand(const tstring& command) const {
return get_value(m_command_lowercase_to_command, lowercase(command));
}
tstring Plugins::UseUnicodeVariant(const tstring& command) const {
return get_value(m_unicode_variant, lowercase(command), command);
}
int Plugins::GetPluginHandle(bool uninst, const tstring& command) const {
if (uninst) {
return get_value(m_command_to_uninstall_data_handle, command, -1);

View file

@ -30,6 +30,7 @@ class Plugins
tstring NormalizedCommand(const tstring& command) const;
int GetPluginHandle(bool uninst, const tstring& command) const;
tstring GetPluginPath(const tstring& command) const;
tstring UseUnicodeVariant(const tstring& lcsig) const;
void SetDllDataHandle(bool uninst, const tstring& command, int dataHandle);
private: // methods
@ -38,6 +39,7 @@ class Plugins
private: // data members
std::map<tstring, tstring> m_command_lowercase_to_command;
std::map<tstring, tstring> m_command_to_path;
std::map<tstring, tstring> m_unicode_variant;
std::map<tstring, int> m_command_to_data_handle;
std::map<tstring, int> m_command_to_uninstall_data_handle;
};

View file

@ -116,12 +116,8 @@ CEXEBuild::CEXEBuild() :
definedlist.add(_T("NSIS_VERSION"), NSIS_VERSION);
#ifdef _UNICODE
definedlist.add(_T("NSIS_UNICODE"));
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("2"));
#else
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1"));
#endif
build_unicode=false;
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1")); // this can change after a UnicodeInstaller instruction is found
// automatically generated header file containing all defines
#include <nsis-defines.h>
@ -230,8 +226,8 @@ CEXEBuild::CEXEBuild() :
uninstaller_writes_used=0;
build_strlist.add(_T(""),0);
ubuild_strlist.add(_T(""),0);
build_strlist.add(_T(""), CP_ACP, false, build_unicode);
ubuild_strlist.add(_T(""), CP_ACP, false, build_unicode);
build_langstring_num=0;
ubuild_langstring_num=0;
@ -469,11 +465,11 @@ int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, WORD codepage/
if (idx < 0) return idx;
}
if (!process) return cur_strlist->add(string,2);
if (!process) return cur_strlist->add(string, codepage, false, build_unicode);
TCHAR buf[NSIS_MAX_STRLEN*4];
preprocess_string(buf,string,codepage);
return cur_strlist->add(buf,2);
return cur_strlist->add(buf,codepage, true, build_unicode);
}
int CEXEBuild::add_intstring(const int i) // returns offset in stringblock
@ -483,6 +479,35 @@ int CEXEBuild::add_intstring(const int i) // returns offset in stringblock
return add_string(i_str);
}
#ifdef _UNICODE
char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage)
{
const TCHAR *p=in;
for (;;)
{
_TUCHAR i = (_TUCHAR)*p++;
if (NS_IS_CODE(i)) // Note: this include '\0'
{
// convert all character up to, and including this code
int cb = WideCharToMultiByte(codepage, 0, in, p-in, out, (p-in)*2, NULL, NULL);
out += cb;
if (i == _T('\0'))
break;
else if (i == NS_SKIP_CODE)
*out++ = (char) *in++; // simply copy escaped code (01..04)
else
{
WORD w = *p++; // special NSIS code is following by a WORD we need to output unchanged
*out++ = LOBYTE(w);
*out++ = HIBYTE(w);
}
in = p;
}
}
return out;
}
#endif
// based on Dave Laundon's code
int CEXEBuild::preprocess_string(TCHAR *out, const TCHAR *in, WORD codepage/*=CP_ACP*/)
{
@ -1498,7 +1523,7 @@ int CEXEBuild::resolve_coderefs(const TCHAR *str)
else
{
// normal string
section_name = cur_strlist->get() + x;
section_name = cur_strlist->getTchar() + x;
}
if (x) wsprintf(fname,_T("%s section \"%s\" (%d)"),str,section_name,cnt);
else wsprintf(fname,_T("unnamed %s section (%d)"),str,cnt);
@ -2080,7 +2105,7 @@ again:
#define REMOVE_ICON(id) if (disable_window_icon) { \
BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \
if (dlg) { \
CDialogTemplate dt(dlg,uDefCodePage); \
CDialogTemplate dt(dlg,build_unicode,uDefCodePage); \
res_editor->FreeResource(dlg); \
if (dt.RemoveItem(IDC_ULICON)) { \
DialogItemTemplate* text = dt.GetItem(IDC_INTROTEXT); \
@ -2210,7 +2235,7 @@ void CEXEBuild::AddStandardStrings()
void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf)
{
GrowBuf blocks_buf;
growbuf_writer_sink sink(&blocks_buf);
growbuf_writer_sink sink(&blocks_buf, build_unicode);
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
cur_header->blocks[NB_PAGES].offset = sizeof(header) + blocks_buf.getlen();
@ -2224,7 +2249,12 @@ void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf)
entry_writer::write_block(cur_entries, &sink);
cur_header->blocks[NB_STRINGS].offset = sizeof(header) + blocks_buf.getlen();
blocks_buf.add(cur_strlist->get(), cur_strlist->getcount()*sizeof(TCHAR));
#ifdef _UNICODE
if (!build_unicode)
blocks_buf.add(cur_strlist->getAnsi(), cur_strlist->getcount());
else
#endif
blocks_buf.add(cur_strlist->getTchar(), cur_strlist->getcount()*sizeof(TCHAR));
cur_header->blocks[NB_LANGTABLES].offset = sizeof(header) + blocks_buf.getlen();
lang_table_writer::write_block(cur_langtables, &sink, cur_header->langtable_size);
@ -2244,7 +2274,7 @@ void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf)
}
#endif
growbuf_writer_sink sink2(hdrbuf);
growbuf_writer_sink sink2(hdrbuf, build_unicode);
header_writer header(&sink2);
header.write(cur_header);
@ -2259,7 +2289,8 @@ int CEXEBuild::SetVarsSection()
VerifyDeclaredUserVarRefs(&m_UserVarNames);
int MaxUserVars = m_UserVarNames.getnum();
// -1 because the default size is 1
if (!res_editor->AddExtraVirtualSize2PESection(NSIS_VARS_SECTION, (MaxUserVars - 1) * sizeof(NSIS_STRING)))
int stringSize = NSIS_MAX_STRLEN*(build_unicode?sizeof(TCHAR):sizeof(char));
if (!res_editor->AddExtraVirtualSize2PESection(NSIS_VARS_SECTION, (MaxUserVars - 1) * stringSize))
{
ERROR_MSG(_T("Internal compiler error #12346: invalid exehead cannot find section \"%s\"!\n"), _T(NSIS_VARS_SECTION));
return PS_ERROR;
@ -2658,7 +2689,7 @@ int CEXEBuild::write_output(void)
int ne=build_header.blocks[NB_ENTRIES].num;
INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ne*sizeof(entry));
int ns=build_strlist.getnum();
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.getcount()*sizeof(TCHAR));
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR)));
int nlt=build_header.blocks[NB_LANGTABLES].num;
INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),build_langtables.getlen());
if (ubuild_entries.getlen())
@ -2687,7 +2718,7 @@ int CEXEBuild::write_output(void)
ne=build_uninst.blocks[NB_ENTRIES].num;
INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ubuild_entries.getlen());
ns=ubuild_strlist.getnum();
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.getcount()*sizeof(TCHAR));
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR)));
nlt=build_uninst.blocks[NB_LANGTABLES].num;
INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),ubuild_langtables.getlen());
}
@ -3014,7 +3045,7 @@ int CEXEBuild::uninstall_generate()
MMapBuf udata;
{
growbuf_writer_sink sink(&udata);
growbuf_writer_sink sink(&udata, build_unicode);
firstheader_writer w(&sink);
w.write(&fh);
}
@ -3532,12 +3563,40 @@ void CEXEBuild::VerifyDeclaredUserVarRefs(UserVarsStringList *pVarsStringList)
}
}
int CEXEBuild::set_compressor(const tstring& compressor, const bool solid) {
tstring stub = stubs_dir + PLATFORM_PATH_SEPARATOR_STR + compressor;
if (solid)
stub += _T("_solid");
#ifdef _UNICODE
int CEXEBuild::set_build_unicode(bool unicode_installer)
{
build_unicode = unicode_installer;
definedlist.del(_T("NSIS_UNICODE"));
definedlist.del(_T("NSIS_CHAR_SIZE"));
if (unicode_installer) // update defines depending on target installer type
{
definedlist.add(_T("NSIS_UNICODE"));
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("2"));
}
else
{
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1"));
}
return load_stub();
}
#endif
return update_exehead(stub, &m_exehead_original_size);
int CEXEBuild::set_compressor(const tstring& compressor, const bool solid) {
stub_filename = stubs_dir + PLATFORM_PATH_SEPARATOR_STR + compressor;
if (solid)
stub_filename += _T("_solid");
return load_stub();
}
int CEXEBuild::load_stub()
{
#ifdef _UNICODE
if (build_unicode)
return update_exehead(stub_filename+_T('W'), &m_exehead_original_size);
else
#endif
return update_exehead(stub_filename, &m_exehead_original_size);
}
int CEXEBuild::update_exehead(const tstring& file, size_t *size/*=NULL*/) {

View file

@ -136,7 +136,9 @@ class CEXEBuild {
int prepare_uninstaller();
int pack_exe_header();
int set_build_unicode(bool unicode_installer);
int set_compressor(const tstring& compressor, const bool solid);
int load_stub();
int update_exehead(const tstring& file, size_t *size=NULL);
void update_exehead(const unsigned char *new_exehead, size_t new_size);
@ -377,6 +379,7 @@ class CEXEBuild {
// a whole bunch O data.
tstring stubs_dir;
tstring stub_filename;
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
ICompressor *compressor;
@ -392,6 +395,7 @@ class CEXEBuild {
int build_compress_dict_size;
bool no_space_texts;
bool build_unicode;
bool has_called_write_output;
@ -442,7 +446,7 @@ class CEXEBuild {
GrowBuf build_instruction_entry_map,ubuild_instruction_entry_map, *cur_instruction_entry_map;
TinyGrowBuf build_functions, ubuild_functions, *cur_functions;
TinyGrowBuf build_labels, ubuild_labels, *cur_labels;
StringList build_strlist, ubuild_strlist, *cur_strlist;
MLStringList build_strlist, ubuild_strlist, *cur_strlist;
GrowBuf build_langtables, ubuild_langtables, *cur_langtables;
TinyGrowBuf build_pages, ubuild_pages, *cur_pages;
TinyGrowBuf build_ctlcolors, ubuild_ctlcolors, *cur_ctlcolors;

View file

@ -722,7 +722,7 @@ DWORD CALLBACK StreamLicense(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
return 0;
}
#ifdef _UNICODE
// on-the-fly conversion of Unicode to ANSI (because Windows don't recognize Unicode RTF data)
// on-the-fly conversion of Unicode to ANSI (because Windows doesn't recognize Unicode RTF data)
DWORD CALLBACK StreamLicenseRTF(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{
size_t len = lstrlen(((LPWSTR) dwCookie)+dwRead);

View file

@ -489,17 +489,10 @@ typedef struct {
// We are doing this to store an integer value into a char string and we
// don't want false end of string values
#if _UNICODE
#define CODE_SHORT(x) ((WORD)x + 1)
#define MAX_CODED 0xFFFE
// This macro takes a pointer to WCHAR
#define DECODE_SHORT(c) (c[0]-1)
#else
#define CODE_SHORT(x) (WORD)((((WORD)(x) & 0x7F) | (((WORD)(x) & 0x3F80) << 1) | 0x8080))
#define MAX_CODED 0x3FFF
// This macro takes a pointer to CHAR
#define DECODE_SHORT(c) (((c[1] & 0x7F) << 7) | (c[0] & 0x7F))
#endif
#define DECODE_SHORT(c) (((((char*)c)[1] & 0x7F) << 7) | (((char*)c)[0] & 0x7F))
#define NSIS_INSTDIR_INVALID 1
#define NSIS_INSTDIR_NOT_ENOUGH_SPACE 2

View file

@ -381,15 +381,10 @@ const TCHAR *CEXEBuild::GetLangNameAndCP(LANGID lang, unsigned int *codepage/*=N
return table->nlf.m_szName;
}
else {
// If the language table does not exist, then we default to Unicode in the
// Unicode version and English in the ANSI version.
#ifdef _UNICODE
// If the language table does not exist, then we default to Unicode or ANSI
// depending on the target installer type
if (codepage)
*codepage = 1200; // Unicode
#else
if (codepage)
*codepage = 1252; // ANSI CP1252
#endif
*codepage = build_unicode ? 1200 : 1252; // Unicode or CP1252
if (lang == 1033)
return _T("English");
@ -703,7 +698,7 @@ int CEXEBuild::GenerateLangTables() {
#define ADD_FONT(id) { \
BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \
if (dlg) { \
CDialogTemplate td(dlg); \
CDialogTemplate td(dlg,build_unicode); \
res_editor->FreeResource(dlg); \
td.SetFont(build_font, (WORD) build_font_size); \
DWORD dwSize; \
@ -758,7 +753,7 @@ int CEXEBuild::GenerateLangTables() {
#define ADD_FONT(id) { \
BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \
if (dlg) { \
CDialogTemplate td(dlg,lt[i].nlf.m_uCodePage); \
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) { \

View file

@ -867,12 +867,10 @@ int CEXEBuild::LoadLicenseFile(TCHAR *file, TCHAR** pdata, LineParser &line, BOO
}
if (!memcmp(data+1,_T("{\\rtf"),5*sizeof(TCHAR)))
*data = SF_RTF;
else
#ifdef _UNICODE
else if (unicode)
*data = SF_TEXT|SF_UNICODE;
#else
else
*data = SF_TEXT;
#endif
return PS_OK;
}
@ -2155,7 +2153,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
BYTE* dlg = res_editor->GetResource(RT_DIALOG, IDD_INSTFILES, NSIS_DEFAULT_LANG);
if (!dlg) throw runtime_error("IDD_INSTFILES doesn't exist!");
CDialogTemplate dt(dlg,uDefCodePage);
CDialogTemplate dt(dlg,build_unicode,uDefCodePage);
free(dlg);
DialogItemTemplate* progress = dt.GetItem(IDC_PROGRESS);
if (!progress) {
@ -2448,7 +2446,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
init_res_editor();
// Search for required items
#define GET(x) dlg = uire->GetResource(RT_DIALOG, x, 0); if (!dlg) return PS_ERROR; CDialogTemplate UIDlg(dlg, uDefCodePage);
#define GET(x) dlg = uire->GetResource(RT_DIALOG, x, 0); if (!dlg) return PS_ERROR; CDialogTemplate UIDlg(dlg, build_unicode, uDefCodePage);
#define SEARCH(x) if (!UIDlg.GetItem(x)) {ERROR_MSG(_T("Error: Can't find %s (%u) in the custom UI!\n"), _T(#x), x);delete [] dlg;delete uire;return PS_ERROR;}
#define SAVE(x) dwSize = UIDlg.GetSize(); res_editor->UpdateResource(RT_DIALOG, x, NSIS_DEFAULT_LANG, dlg, dwSize); delete [] dlg;
@ -2568,7 +2566,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
init_res_editor();
BYTE* dlg = res_editor->GetResource(RT_DIALOG, IDD_INST, NSIS_DEFAULT_LANG);
CDialogTemplate dt(dlg, uDefCodePage);
CDialogTemplate dt(dlg, build_unicode, uDefCodePage);
res_editor->FreeResource(dlg);
@ -2687,6 +2685,25 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
}
return PS_OK;
#ifdef _UNICODE
case TOK_UNICODEINSTALLER:
{
if (build_compressor_set) {
ERROR_MSG(_T("Error: can't change type of installer after data already got compressed or header already changed!\n"));
return PS_ERROR;
}
int param=line.gettoken_enum(1,_T("off\0on\0"));
if (param==-1) PRINTHELP()
SCRIPT_MSG(_T("UnicodeInstaller: %s\n"),line.gettoken_str(1));
if (set_build_unicode(param != 0) != PS_OK)
{
ERROR_MSG(_T("Error: error while setting type of installer! (stub not found?)\n"));
return PS_ERROR;
}
}
return PS_OK;
#endif
// Ability to change compression methods from within the script
case TOK_SETCOMPRESSOR:
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
@ -3681,7 +3698,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
init_res_editor();
BYTE* dlg = res_editor->GetResource(RT_DIALOG, IDD_INST, NSIS_DEFAULT_LANG);
CDialogTemplate td(dlg,uDefCodePage);
CDialogTemplate td(dlg,build_unicode,uDefCodePage);
free(dlg);
if (trim) {
@ -5913,8 +5930,13 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
{
int ret;
const tstring command = m_plugins.NormalizedCommand(line.gettoken_str(0));
const tstring dllPath = m_plugins.GetPluginPath(command);
tstring command = m_plugins.NormalizedCommand(line.gettoken_str(0));
#ifdef _UNICODE
if (build_unicode)
command = m_plugins.UseUnicodeVariant(command);
#endif
tstring dllPath = m_plugins.GetPluginPath(command);
tstring dllName = get_file_name(dllPath);
int data_handle = m_plugins.GetPluginHandle(uninstall_mode?true:false, command);
if (uninstall_mode) uninst_plugin_used = true;
@ -5930,7 +5952,6 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
// DLL name on the user machine
TCHAR tempDLL[NSIS_MAX_STRLEN];
tstring dllName = get_file_name(dllPath);
wsprintf(tempDLL, _T("$PLUGINSDIR\\%s"), dllName.c_str());
// Add the DLL to the installer

View file

@ -18,6 +18,97 @@
#include "strlist.h"
MLStringList::MLStringList()
{
m_gr.set_zeroing(1);
#ifdef _UNICODE
m_grAnsi.set_zeroing(1);
#endif
}
#ifdef _UNICODE
char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage); // defined in build.cpp
// use 2 for case sensitive end-of-string matches too
int MLStringList::findAnsi(const char *str, int case_sensitive) const // returns -1 if not found
{
const char *s=(const char*) m_grAnsi.get();
int ml=getcount();
int offs=0;
size_t str_slen = strlen(str);
size_t offs_slen;
while (offs < ml)
{
// Check if the whole string matches str.
if ((case_sensitive && !strcmp(s+offs,str)) ||
(!case_sensitive && !stricmp(s+offs,str)))
{
return offs;
}
offs_slen = strlen(s+offs);
// Check if just the end of the string matches str.
if (case_sensitive==2 &&
str_slen < offs_slen && // check for end of string
!strcmp(s + offs + offs_slen - str_slen,str))
{
return offs + offs_slen - str_slen;
}
offs += offs_slen + 1;
}
return -1;
}
#endif
int MLStringList::add(const TCHAR *str, WORD codepage /*= CP_ACP*/, bool processed, bool build_unicode)
{
#ifndef _UNICODE
int a=find(str,2);
if (a >= 0)
return a;
int len = _tcslen(str)+1;
return m_gr.add(str,len*sizeof(TCHAR))/sizeof(TCHAR);
#else
if (build_unicode)
{
int a=find(str,2);
if (a >= 0)
return a;
}
// convert to ANSI
int len = _tcslen(str)+1;
char* ansiBuf = new char[len*2];
int cbMultiByte;
if (processed)
cbMultiByte = convert_processed_string_to_ansi(ansiBuf, str, codepage)-ansiBuf;
else
cbMultiByte = WideCharToMultiByte(codepage, 0, str, len, ansiBuf, len*2, NULL, NULL);
if (!build_unicode)
{
int a=findAnsi(ansiBuf,2);
if (a >= 0)
{
delete[] ansiBuf;
return a;
}
}
// string not found, add it
int a=m_gr.add(str,len*sizeof(TCHAR))/sizeof(TCHAR);
m_grAnsi.add(ansiBuf,cbMultiByte);
delete[] ansiBuf;
if (len != cbMultiByte)
{ // resize buffers to align future strings on same offsets
len = a+max(len,cbMultiByte);
m_gr.resize(len*sizeof(TCHAR));
m_grAnsi.resize(len);
}
return a;
#endif
}
int StringList::add(const TCHAR *str, int case_sensitive)
{
int a=find(str,case_sensitive);
@ -103,15 +194,6 @@ int StringList::getnum() const
return idx;
}
const TCHAR *StringList::get() const
{
return (const TCHAR*) m_gr.get();
}
int StringList::getcount() const
{
return m_gr.getlen() / sizeof(TCHAR);
}
// ==========
// DefineList

View file

@ -104,18 +104,43 @@ public:
* Get the buffer straight as a const TCHAR pointer. Very unwise to use.
* @return m_gr.m_s cast as a TCHAR*.
*/
const TCHAR *get() const;
const TCHAR *get() const { return (const TCHAR*) m_gr.get(); }
/**
* Get the buffer size (number of TCHARs).
* @return The buffer size (number of TCHARs).
*/
int getcount() const;
int getcount() const { return m_gr.getlen() / sizeof(TCHAR); }
private:
protected:
GrowBuf m_gr;
};
/**
* Similar to StringList with case_sensitive=2, but stores strings as both Unicode AND ANSI (codepaged)
*/
class MLStringList : private StringList
{
private: // don't copy instances
MLStringList(const MLStringList&);
void operator=(const MLStringList&);
public:
MLStringList();
~MLStringList() {}
int add(const TCHAR *str, WORD codepage, bool processed, bool build_unicode);
int getnum() const { return StringList::getnum(); }
int getcount() const { return StringList::getcount(); }
const TCHAR *getTchar() const { return (const TCHAR*) m_gr.get(); }
#ifdef _UNICODE
const char *getAnsi() const { return (const char*) m_grAnsi.get(); }
int findAnsi(const char *str, int case_sensitive) const;
private:
GrowBuf m_grAnsi;
#endif
};
/**
* This class maintains a list of T types in a GrowBuf sorted by T.name which
* is assumed to be a string (TCHAR*). So it's really sort of a

View file

@ -216,6 +216,9 @@ static tokenType tokenlist[TOK__LAST] =
{TOK_STRCPY,_T("StrCpy"),2,2,_T("$(user_var: output) str [maxlen] [startoffset]"),TP_CODE},
{TOK_STRLEN,_T("StrLen"),2,0,_T("$(user_var: length output) str"),TP_CODE},
{TOK_SUBCAPTION,_T("SubCaption"),2,0,_T("page_number(0-4) new_subcaption"),TP_GLOBAL},
#ifdef _UNICODE
{TOK_UNICODEINSTALLER,_T("UnicodeInstaller"),1,0,_T("(on|off)"),TP_GLOBAL},
#endif
{TOK_UNINSTALLEXENAME,_T("UninstallExeName"),0,0,_T("no longer supported, use WriteUninstaller from section."),TP_ALL},
{TOK_UNINSTCAPTION,_T("UninstallCaption"),1,0,_T("uninstaller_caption"),TP_GLOBAL},
{TOK_UNINSTICON,_T("UninstallIcon"),1,0,_T("icon_on_local_system.ico"),TP_GLOBAL},

View file

@ -70,6 +70,9 @@ enum
TOK_DEFVAR,
TOK_VI_ADDKEY,
TOK_VI_SETPRODUCTVERSION,
#ifdef _UNICODE
TOK_UNICODEINSTALLER,
#endif
TOK_MISCBUTTONTEXT,
TOK_DETAILSBUTTONTEXT,

View file

@ -54,18 +54,29 @@ void writer_sink::write_int_array(const int i[], const size_t len)
void writer_sink::write_string(const TCHAR *s, size_t size)
{
#ifdef _UNICODE
bool strEnd = false;
TCHAR ch;
for (; size ; size--)
if (m_build_unicode)
{
if (!strEnd)
bool strEnd = false;
TCHAR ch;
for (; size ; size--)
{
ch = *s++;
if (ch == _T('\0'))
strEnd = true;
if (!strEnd)
{
ch = *s++;
if (ch == _T('\0'))
strEnd = true;
}
write_short(ch);
}
write_short(ch);
}
else
{
char *wb = new char[size];
memset(wb, 0, size);
WideCharToMultiByte(CP_ACP, 0, s, -1, wb, size, NULL, NULL);
write_data(wb, size);
delete [] wb;
}
#else
char *wb = new char[size];
memset(wb, 0, size);

View file

@ -27,7 +27,7 @@
class writer_sink {
public:
writer_sink() {}
writer_sink() : m_build_unicode(false) {}
virtual ~writer_sink() {}
virtual void write_byte(const unsigned char b);
@ -38,7 +38,8 @@ public:
virtual void write_growbuf(const IGrowBuf *b);
virtual void write_data(const void *data, const size_t size) = 0;
protected:
bool m_build_unicode;
};
class writer {
@ -53,7 +54,7 @@ protected:
class growbuf_writer_sink : public writer_sink {
public:
growbuf_writer_sink(IGrowBuf *buf) : m_buf(buf) {}
growbuf_writer_sink(IGrowBuf *buf, bool build_unicode) : m_buf(buf) { m_build_unicode=build_unicode; }
virtual void write_data(const void *data, const size_t size);