Use a custom file reader with UTF8 support for nsi/nsh/nlf files and store UTF16LE or MBCS (stringblock) strings in ExeHeadStringList

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6289 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2013-03-07 21:25:35 +00:00
parent e6ac4e6d9b
commit dcddf977b2
18 changed files with 1208 additions and 623 deletions

View file

@ -6,13 +6,15 @@ These commands are similar to the C preprocessor in terms of purpose and functio
\S1{include} !include \S1{include} !include
\c [/NONFATAL] file \# NOTE: \NsisInputCharset define cannot be used in a \c block
This command will include 'file' as if it was part of the original script. Note that if a file is included in another directory, the current directory is still where the script was compiled from (not where the included file resides). If the compiler can't find the file it will look for it in every include directory. See \R{addincludedir}{!addincludedir} for more information. If the /nonfatal switch is used and no files are found, a warning will be issued instead of an error. \c [/NONFATAL] [/CHARSET=ACP|OEM|CP#|UTF8] file
This command will include 'file' as if it was part of the original script. Note that if a file is included in another directory, the current directory is still where the script was compiled from (not where the included file resides). If the compiler can't find the file it will look for it in every include directory. See \R{addincludedir}{!addincludedir} for more information. If the /nonfatal switch is used and no files are found, a warning will be issued instead of an error. /charset can be used to specify a codepage for plain text files without a BOM.
\c !include WinMessages.nsh \c !include WinMessages.nsh
\c !include Library.nsh \c !include Library.nsh
\c !include C:\MyConfig.nsi \c !include /CHARSET=CP1252 C:\MyConfig.nsi
\c !include ..\MyConfig.nsh \c !include ..\MyConfig.nsh
\c !include /NONFATAL file_that_may_exist_or_not.nsh \c !include /NONFATAL file_that_may_exist_or_not.nsh

View file

@ -12,6 +12,8 @@
\define{NsisACPcp} system default ANSI codepage (ACP) \define{NsisACPcp} system default ANSI codepage (ACP)
\define{NsisInputCharset} ACP|OEM|CP#|UTF8
\define{NsisWarnBlockContainerBegin} \\<div class=\\"wb\\"\\> \define{NsisWarnBlockContainerBegin} \\<div class=\\"wb\\"\\>
\define{NsisWarnBlockContainerEnd} \\</div\\> \define{NsisWarnBlockContainerEnd} \\</div\\>

View file

@ -29,6 +29,8 @@ If you want to use MakeNSIS on the command line, the syntax of the makensis comm
\b /NOCD disables the current directory change to that of the .nsi file \b /NOCD disables the current directory change to that of the .nsi file
\b /INPUTCHARSET allows you to specify a specific codepage for files without a BOM. (\NsisInputCharset)
\b Using the /D switch one or more times will add to symbols to the globally defined list (See !define). \b Using the /D switch one or more times will add to symbols to the globally defined list (See !define).
\b Using the /X switch one or more times will execute the code you specify following it. Example: "/XAutoCloseWindow false" \b Using the /X switch one or more times will execute the code you specify following it. Example: "/XAutoCloseWindow false"

View file

@ -88,3 +88,14 @@ int ConstantsStringList::get_internal_idx(int idx)
} }
return -1; return -1;
} }
bool ConstantsStringList::set_values(const TCHAR *name, int val1, int val2)
{
int v = SortedStringListND<struct constantstring>::find(name, -1);
if (-1 == v) return false;
struct constantstring & cs = ((struct constantstring*) m_gr.get())[v];
cs.value1 = val1;
cs.value2 = val2;
return true;
}

View file

@ -40,6 +40,7 @@ class ConstantsStringList : public SortedStringListND<struct constantstring>
int get_value1(int idx); int get_value1(int idx);
int get_value2(int idx); int get_value2(int idx);
TCHAR *idx2name(int idx); TCHAR *idx2name(int idx);
bool set_values(const TCHAR *name, int val1, int val2);
private: private:
int m_index; int m_index;

View file

@ -101,9 +101,8 @@ CEXEBuild::CEXEBuild() :
m_exehead_size(0) m_exehead_size(0)
{ {
linecnt = 0; linecnt = 0;
fp = 0; curlinereader = 0;
curfilename = 0; curfilename = 0;
curfile_unicode = FALSE;
display_info=1; display_info=1;
display_script=1; display_script=1;
@ -116,9 +115,6 @@ CEXEBuild::CEXEBuild() :
multiple_entries_instruction=0; multiple_entries_instruction=0;
build_include_depth=0; build_include_depth=0;
#ifndef _UNICODE
build_include_isutf8=false;
#endif
has_called_write_output=false; has_called_write_output=false;
@ -131,6 +127,7 @@ CEXEBuild::CEXEBuild() :
#endif #endif
build_unicode=false; build_unicode=false;
build_lockedunicodetarget=false;
m_target_type=TARGET_X86ANSI; m_target_type=TARGET_X86ANSI;
// automatically generated header file containing all defines // automatically generated header file containing all defines
@ -244,8 +241,8 @@ CEXEBuild::CEXEBuild() :
uninstaller_writes_used=0; uninstaller_writes_used=0;
build_strlist.add(_T(""), CP_ACP, false, build_unicode); build_strlist.addemptystring();
ubuild_strlist.add(_T(""), CP_ACP, false, build_unicode); ubuild_strlist.addemptystring();
build_langstring_num=0; build_langstring_num=0;
ubuild_langstring_num=0; ubuild_langstring_num=0;
@ -365,77 +362,17 @@ CEXEBuild::CEXEBuild() :
m_ShellConstants.add(_T("RESOURCES"), CSIDL_RESOURCES, CSIDL_RESOURCES); m_ShellConstants.add(_T("RESOURCES"), CSIDL_RESOURCES, CSIDL_RESOURCES);
m_ShellConstants.add(_T("RESOURCES_LOCALIZED"), CSIDL_RESOURCES_LOCALIZED, CSIDL_RESOURCES_LOCALIZED); m_ShellConstants.add(_T("RESOURCES_LOCALIZED"), CSIDL_RESOURCES_LOCALIZED, CSIDL_RESOURCES_LOCALIZED);
m_ShellConstants.add(_T("CDBURN_AREA"), CSIDL_CDBURN_AREA, CSIDL_CDBURN_AREA); m_ShellConstants.add(_T("CDBURN_AREA"), CSIDL_CDBURN_AREA, CSIDL_CDBURN_AREA);
// PROGRAMFILES&COMMONFILES does a registry lookup and the required string offsets are filled in later.
unsigned int program_files = add_string(_T("ProgramFilesDir"), 0); // We do this because the unicode mode has to be locked when we call add_string...
unsigned int program_files_def = add_string(_T("C:\\Program Files")); m_ShellConstants.add(_T("PROGRAMFILES"), 0, 0);
m_ShellConstants.add(_T("PROGRAMFILES32"), 0, 0);
if ((program_files >= 0x40) || (program_files_def >= 0xFF)) m_ShellConstants.add(_T("PROGRAMFILES64"), 0, 0);
{ m_ShellConstants.add(_T("COMMONFILES"), 0, 0);
// see Source\exehead\util.c for implementation details m_ShellConstants.add(_T("COMMONFILES32"), 0, 0);
// basically, it knows it needs to get folders from the registry when the 0x80 is on m_ShellConstants.add(_T("COMMONFILES64"), 0, 0);
ERROR_MSG(_T("Internal compiler error: too many strings added to strings block before adding shell constants!\n"));
throw out_of_range("Internal compiler error: too many strings added to strings block before adding shell constants!");
}
m_ShellConstants.add(_T("PROGRAMFILES"), 0x80 | program_files, program_files_def);
unsigned int program_files64_def = add_string(_T("$PROGRAMFILES"));
if (program_files64_def > 0xFF)
{
ERROR_MSG(_T("Internal compiler error: too many strings added to strings block before adding shell constants!\n"));
throw out_of_range("Internal compiler error: too many strings added to strings block before adding shell constants!");
}
m_ShellConstants.add(_T("PROGRAMFILES32"), 0x80 | program_files, program_files_def);
m_ShellConstants.add(_T("PROGRAMFILES64"), 0xC0 | program_files, program_files64_def);
unsigned int common_files = add_string(_T("CommonFilesDir"), 0);
unsigned int common_files_def = add_string(_T("$PROGRAMFILES\\Common Files"));
if ((common_files > 0x40) || (common_files_def > 0xFF))
{
ERROR_MSG(_T("Internal compiler error: too many strings added to strings block before adding shell constants!\n"));
throw out_of_range("Internal compiler error: too many strings added to strings block before adding shell constants!");
}
m_ShellConstants.add(_T("COMMONFILES"), 0x80 | common_files, common_files_def);
unsigned int common_files64_def = add_string(_T("$COMMONFILES"));
if (common_files64_def > 0xFF)
{
ERROR_MSG(_T("Internal compiler error: too many strings added to strings block before adding shell constants!\n"));
throw out_of_range("Internal compiler error: too many strings added to strings block before adding shell constants!");
}
m_ShellConstants.add(_T("COMMONFILES32"), 0x80 | common_files, common_files_def);
m_ShellConstants.add(_T("COMMONFILES64"), 0xC0 | common_files, common_files64_def);
set_uninstall_mode(1);
unsigned int uprogram_files = add_string(_T("ProgramFilesDir"), 0);
unsigned int uprogram_files_def = add_string(_T("C:\\Program Files"));
unsigned int uprogram_files64_def = add_string(_T("$PROGRAMFILES"));
unsigned int ucommon_files = add_string(_T("CommonFilesDir"), 0);
unsigned int ucommon_files_def = add_string(_T("$PROGRAMFILES\\Common Files"));
unsigned int ucommon_files64_def = add_string(_T("$COMMONFILES"));
if (uprogram_files != program_files
|| uprogram_files_def != program_files_def
|| uprogram_files64_def != program_files64_def
|| ucommon_files != common_files
|| ucommon_files_def != common_files_def
|| ucommon_files64_def != common_files64_def)
{
ERROR_MSG(_T("Internal compiler error: installer's shell constants are different than uninstallers!\n"));
throw out_of_range("Internal compiler error: installer's shell constants are different than uninstallers!");
}
set_uninstall_mode(0); set_uninstall_mode(0);
set_code_type_predefines(); set_code_type_predefines();
set_target_architecture_predefines(); set_target_architecture_predefines();
} }
@ -472,12 +409,75 @@ void CEXEBuild::initialize(const TCHAR *makensis_path)
int CEXEBuild::getcurdbsize() { return cur_datablock->getlen(); } int CEXEBuild::getcurdbsize() { return cur_datablock->getlen(); }
void CEXEBuild::init_shellconstantvalues()
{
static bool done = false;
if (done) return ;
done = true;
// Note: The order matters because some of the strings are preprocessed and cf must be <= 0x40
unsigned int pf = add_asciistring(_T("ProgramFilesDir"), 0);
unsigned int cf = add_asciistring(_T("CommonFilesDir"), 0);
unsigned int pf_def = add_asciistring(_T("C:\\Program Files"));
m_ShellConstants.set_values(_T("PROGRAMFILES"), 0x80 | pf, pf_def);
unsigned int pf64_def = add_asciistring(_T("$PROGRAMFILES"));
m_ShellConstants.set_values(_T("PROGRAMFILES32"), 0x80 | pf, pf_def);
m_ShellConstants.set_values(_T("PROGRAMFILES64"), 0xC0 | pf, pf64_def);
unsigned int cf_def = add_asciistring(_T("$PROGRAMFILES\\Common Files"));
m_ShellConstants.set_values(_T("COMMONFILES"), 0x80 | cf, cf_def);
unsigned int cf64_def = add_asciistring(_T("$COMMONFILES"));
m_ShellConstants.set_values(_T("COMMONFILES32"), 0x80 | cf, cf_def);
m_ShellConstants.set_values(_T("COMMONFILES64"), 0xC0 | cf, cf64_def);
if ( (pf >= 0x40 || pf_def >= 0xFF || pf64_def > 0xFF) // BUGBUG: pf_def should be ">"?
|| (cf > 0x40 || cf_def > 0xFF || cf64_def > 0xFF) )
{
// see Source\exehead\util.c for implementation details
// basically, it knows it needs to get folders from the registry when the 0x80 is on
ERROR_MSG(_T("Internal compiler error: too many strings added to strings block before adding shell constants!\n"));
throw out_of_range("Internal compiler error: too many strings added to strings block before adding shell constants!");
}
const int orgunmode = uninstall_mode;
set_uninstall_mode(1);
unsigned int unpf = add_asciistring(_T("ProgramFilesDir"), 0);
unsigned int uncf = add_asciistring(_T("CommonFilesDir"), 0);
unsigned int unpf_def = add_asciistring(_T("C:\\Program Files"));
unsigned int unpf64_def = add_asciistring(_T("$PROGRAMFILES"));
unsigned int uncf_def = add_asciistring(_T("$PROGRAMFILES\\Common Files"));
unsigned int uncf64_def = add_asciistring(_T("$COMMONFILES"));
set_uninstall_mode(orgunmode);
if ( unpf != pf
|| unpf_def != pf_def
|| unpf64_def != pf64_def
|| uncf != cf
|| uncf_def != cf_def
|| uncf64_def != cf64_def)
{
ERROR_MSG(_T("Internal compiler error: installer's shell constants are different than uninstallers!\n"));
throw out_of_range("Internal compiler error: installer's shell constants are different than uninstallers!");
}
}
// returns offset in stringblock // returns offset in stringblock
int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, WORD codepage/*=CP_ACP*/) int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, UINT codepage/*=-2*/)
{ {
if (!string || !*string) return 0; if (!string || !*string) return 0;
build_lockedunicodetarget = true;
if (*string == _T('$') && *(string+1) == _T('(')) { init_shellconstantvalues();
if (-2 == codepage)
{
assert(curlinereader);
codepage = curlinereader->StreamEncoding().GetCodepage();
// If the current source file is Unicode we have to pick a real codepage for ANSI!
// It might not be the correct codepage but its the best we can do.
// Not using CP_ACP to avoid heisenbugs when compiled on a different system.
if (NStreamEncoding::IsUnicodeCodepage(codepage)) codepage = 1252;
}
if (*string == _T('$') && *(string+1) == _T('('))
{
int idx = 0; int idx = 0;
TCHAR *cp = _tcsdup(string+2); TCHAR *cp = _tcsdup(string+2);
TCHAR *p = _tcschr(cp, _T(')')); TCHAR *p = _tcschr(cp, _T(')'));
@ -489,18 +489,28 @@ int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, WORD codepage/
if (idx < 0) return idx; if (idx < 0) return idx;
} }
if (!process) return cur_strlist->add(string, codepage, false, build_unicode); int i;
if (process)
{
TCHAR buf[NSIS_MAX_STRLEN*4];
preprocess_string(buf, string, codepage);
i = cur_strlist->add(buf, (WORD)codepage, true);
}
else
i = cur_strlist->add(string, (WORD)codepage, false);
return i;
}
TCHAR buf[NSIS_MAX_STRLEN*4]; int CEXEBuild::add_asciistring(const TCHAR *string, int process/*=1*/)
preprocess_string(buf,string,codepage); {
return cur_strlist->add(buf,codepage, true, build_unicode); return add_string(string, process, 1252);
} }
int CEXEBuild::add_intstring(const int i) // returns offset in stringblock int CEXEBuild::add_intstring(const int i) // returns offset in stringblock
{ {
TCHAR i_str[32]; TCHAR buf[32];
wsprintf(i_str, _T("%d"), i); wsprintf(buf, _T("%d"), i);
return add_string(i_str); return add_asciistring(buf, false);
} }
#ifdef _UNICODE #ifdef _UNICODE
@ -514,6 +524,7 @@ char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage
{ {
// convert all character up to, and including this code // convert all character up to, and including this code
int cb = WideCharToMultiByte(codepage, 0, in, p-in, out, (p-in)*2, NULL, NULL); int cb = WideCharToMultiByte(codepage, 0, in, p-in, out, (p-in)*2, NULL, NULL);
if (!cb && i) return 0;
out += cb; out += cb;
if (i == _T('\0')) if (i == _T('\0'))
break; break;
@ -629,6 +640,7 @@ int CEXEBuild::preprocess_string(TCHAR *out, const TCHAR *in, WORD codepage/*=CP
// If found... // If found...
if (idxConst >= 0) if (idxConst >= 0)
{ {
init_shellconstantvalues();
int CSIDL_Value_current = m_ShellConstants.get_value1(idxConst); int CSIDL_Value_current = m_ShellConstants.get_value1(idxConst);
int CSIDL_Value_all = m_ShellConstants.get_value2(idxConst); int CSIDL_Value_all = m_ShellConstants.get_value2(idxConst);
*out++=(TCHAR)NS_SHELL_CODE; // Constant code identifier *out++=(TCHAR)NS_SHELL_CODE; // Constant code identifier
@ -1540,7 +1552,7 @@ int CEXEBuild::resolve_coderefs(const TCHAR *str)
{ {
int x=sec->name_ptr; int x=sec->name_ptr;
TCHAR fname[1024]; TCHAR fname[1024];
const TCHAR *section_name; tstring section_name;
if (x < 0) if (x < 0)
{ {
// lang string // lang string
@ -1549,9 +1561,9 @@ int CEXEBuild::resolve_coderefs(const TCHAR *str)
else else
{ {
// normal string // normal string
section_name = cur_strlist->getTchar() + x; cur_strlist->get(x,section_name);
} }
if (x) wsprintf(fname,_T("%s section \"%s\" (%d)"),str,section_name,cnt); if (x) wsprintf(fname,_T("%s section \"%s\" (%d)"),str,section_name.c_str(),cnt);
else wsprintf(fname,_T("unnamed %s section (%d)"),str,cnt); else wsprintf(fname,_T("unnamed %s section (%d)"),str,cnt);
for (x = sec->code; x < sec->code+sec->code_size; x ++) for (x = sec->code; x < sec->code+sec->code_size; x ++)
{ {
@ -2253,12 +2265,12 @@ void CEXEBuild::AddStandardStrings()
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT #ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (uninstall_mode) if (uninstall_mode)
{ {
cur_header->str_uninstchild = add_string(_T("$TEMP\\$1u_.exe")); cur_header->str_uninstchild = add_asciistring(_T("$TEMP\\$1u_.exe"));
cur_header->str_uninstcmd = add_string(_T("\"$TEMP\\$1u_.exe\" $0 _?=$INSTDIR\\")); cur_header->str_uninstcmd = add_asciistring(_T("\"$TEMP\\$1u_.exe\" $0 _?=$INSTDIR\\"));
} }
#endif//NSIS_CONFIG_UNINSTALL_SUPPORT #endif//NSIS_CONFIG_UNINSTALL_SUPPORT
#ifdef NSIS_SUPPORT_MOVEONREBOOT #ifdef NSIS_SUPPORT_MOVEONREBOOT
cur_header->str_wininit = add_string(_T("$WINDIR\\wininit.ini")); cur_header->str_wininit = add_asciistring(_T("$WINDIR\\wininit.ini"));
#endif//NSIS_SUPPORT_MOVEONREBOOT #endif//NSIS_SUPPORT_MOVEONREBOOT
} }
@ -2279,12 +2291,7 @@ void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf)
entry_writer::write_block(cur_entries, &sink); entry_writer::write_block(cur_entries, &sink);
cur_header->blocks[NB_STRINGS].offset = sizeof(header) + blocks_buf.getlen(); cur_header->blocks[NB_STRINGS].offset = sizeof(header) + blocks_buf.getlen();
#ifdef _UNICODE blocks_buf.add(cur_strlist->getstorageptr(), cur_strlist->gettotalsize());
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(); cur_header->blocks[NB_LANGTABLES].offset = sizeof(header) + blocks_buf.getlen();
lang_table_writer::write_block(cur_langtables, &sink, cur_header->langtable_size); lang_table_writer::write_block(cur_langtables, &sink, cur_header->langtable_size);
@ -2724,7 +2731,7 @@ int CEXEBuild::write_output(void)
int ne=build_header.blocks[NB_ENTRIES].num; 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)); INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ne*sizeof(entry));
int ns=build_strlist.getnum(); int ns=build_strlist.getnum();
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR))); INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.gettotalsize());
int nlt=build_header.blocks[NB_LANGTABLES].num; 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()); INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),build_langtables.getlen());
if (ubuild_entries.getlen()) if (ubuild_entries.getlen())
@ -2753,7 +2760,7 @@ int CEXEBuild::write_output(void)
ne=build_uninst.blocks[NB_ENTRIES].num; ne=build_uninst.blocks[NB_ENTRIES].num;
INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ubuild_entries.getlen()); INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ubuild_entries.getlen());
ns=ubuild_strlist.getnum(); ns=ubuild_strlist.getnum();
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR))); INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.gettotalsize());
nlt=build_uninst.blocks[NB_LANGTABLES].num; 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()); INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),ubuild_langtables.getlen());
} }
@ -3460,14 +3467,14 @@ again:
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
// don't move this, depends on [un.] // don't move this, depends on [un.]
zero_offset=add_string(_T("$0")); zero_offset=add_asciistring(_T("$0"));
// SetDetailsPrint none // SetDetailsPrint none
ret=add_entry_direct(EW_SETFLAG, FLAG_OFFSET(status_update), add_intstring(6)); ret=add_entry_direct(EW_SETFLAG, FLAG_OFFSET(status_update), add_intstring(6));
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
// StrCmp $PLUGINSDIR "" // StrCmp $PLUGINSDIR ""
ret=add_entry_direct(EW_STRCMP, add_string(_T("$PLUGINSDIR")), 0, 0, ns_label.add(_T("Initialize_____Plugins_done"),0)); ret=add_entry_direct(EW_STRCMP, add_asciistring(_T("$PLUGINSDIR")), 0, 0, ns_label.add(_T("Initialize_____Plugins_done"),0));
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
// Push $0 // Push $0
ret=add_entry_direct(EW_PUSHPOP, zero_offset); ret=add_entry_direct(EW_PUSHPOP, zero_offset);
@ -3476,7 +3483,7 @@ again:
ret=add_entry_direct(EW_SETFLAG, FLAG_OFFSET(exec_error)); ret=add_entry_direct(EW_SETFLAG, FLAG_OFFSET(exec_error));
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
// GetTempFileName $0 // GetTempFileName $0
ret=add_entry_direct(EW_GETTEMPFILENAME, var_zero, add_string(_T("$TEMP"))); ret=add_entry_direct(EW_GETTEMPFILENAME, var_zero, add_asciistring(_T("$TEMP")));
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
// Delete $0 [simple, nothing that could clash with special temp permissions] // Delete $0 [simple, nothing that could clash with special temp permissions]
ret=add_entry_direct(EW_DELETEFILE, zero_offset, DEL_SIMPLE); ret=add_entry_direct(EW_DELETEFILE, zero_offset, DEL_SIMPLE);
@ -3503,7 +3510,7 @@ again:
// error // error
if (add_label(_T("Initialize_____Plugins_error"))) return PS_ERROR; if (add_label(_T("Initialize_____Plugins_error"))) return PS_ERROR;
// error message box // error message box
ret=add_entry_direct(EW_MESSAGEBOX, MB_OK|MB_ICONSTOP|(IDOK<<21), add_string(_T("Error! Can't initialize plug-ins directory. Please try again later."))); ret=add_entry_direct(EW_MESSAGEBOX, MB_OK|MB_ICONSTOP|(IDOK<<21), add_asciistring(_T("Error! Can't initialize plug-ins directory. Please try again later.")));
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
// Quit // Quit
ret=add_entry_direct(EW_QUIT); ret=add_entry_direct(EW_QUIT);
@ -3673,7 +3680,10 @@ int CEXEBuild::change_target_architecture()
#ifdef _UNICODE #ifdef _UNICODE
int CEXEBuild::set_target_charset(bool unicode) int CEXEBuild::set_target_charset(bool unicode)
{ {
if (build_lockedunicodetarget) return PS_ERROR;
build_unicode = unicode; build_unicode = unicode;
build_strlist.setunicode(unicode);
ubuild_strlist.setunicode(unicode);
return change_target_architecture(); return change_target_architecture();
} }
#endif #endif

View file

@ -30,6 +30,7 @@
#include "manifest.h" #include "manifest.h"
#include "icon.h" #include "icon.h"
#include <memory.h> #include <memory.h>
#include "utf.h"
#include "exehead/fileform.h" #include "exehead/fileform.h"
#include "exehead/config.h" #include "exehead/config.h"
@ -114,13 +115,13 @@ class CEXEBuild {
// process a script (you can process as many scripts as you want, // process a script (you can process as many scripts as you want,
// it is as if they are concatenated) // it is as if they are concatenated)
int process_script(FILE *filepointer, const TCHAR *filename, BOOL unicode); int process_script(NIStream&Strm, const TCHAR *filename);
int process_oneline(TCHAR *line, const TCHAR *curfilename, int lineptr); int process_oneline(TCHAR *line, const TCHAR *curfilename, int lineptr);
// you only get to call write_output once, so use it wisely. // you only get to call write_output once, so use it wisely.
int write_output(void); int write_output(void);
void print_help(TCHAR *commandname=NULL); void print_help(const TCHAR *commandname=NULL);
DefineList definedlist; // List of identifiers marked as "defined" like DefineList definedlist; // List of identifiers marked as "defined" like
// C++ macro definitions such as _UNICODE. // C++ macro definitions such as _UNICODE.
@ -133,8 +134,7 @@ class CEXEBuild {
int linecnt; int linecnt;
const TCHAR *curfilename; const TCHAR *curfilename;
BOOL curfile_unicode; NStreamLineReader* curlinereader;
FILE *fp;
HWND notify_hwnd; HWND notify_hwnd;
void notify(notify_e code, const TCHAR *data) const; void notify(notify_e code, const TCHAR *data) const;
@ -176,9 +176,9 @@ class CEXEBuild {
void del_date_time_predefines(); void del_date_time_predefines();
#endif #endif
int parseScript(); int parseScript();
int includeScript(TCHAR *f); int includeScript(const TCHAR *f, NStreamEncoding&enc);
int MacroExists(const TCHAR *macroname); int MacroExists(const TCHAR *macroname);
int LoadLicenseFile(TCHAR *file, TCHAR** pdata, LineParser &line, BOOL* unicode); int LoadLicenseFile(const TCHAR *file, TCHAR** pdata, const TCHAR *cmdname, WORD AnsiCP);
#ifdef NSIS_FIX_DEFINES_IN_STRINGS #ifdef NSIS_FIX_DEFINES_IN_STRINGS
void ps_addtoline(const TCHAR *str, GrowBuf &linedata, StringList &hist, bool bIgnoreDefines = false); void ps_addtoline(const TCHAR *str, GrowBuf &linedata, StringList &hist, bool bIgnoreDefines = false);
#else #else
@ -238,10 +238,12 @@ class CEXEBuild {
int add_db_data(IMMap *map); // returns offset int add_db_data(IMMap *map); // returns offset
int add_db_data(const char *data, int length); // returns offset int add_db_data(const char *data, int length); // returns offset
int add_data(const char *data, int length, IGrowBuf *dblock); // returns offset int add_data(const char *data, int length, IGrowBuf *dblock); // returns offset
int add_string(const TCHAR *string, int process=1, WORD codepage=CP_ACP); // returns offset (in string table) int add_string(const TCHAR *string, int process=1, UINT codepage=-2); // returns offset (in string table)
int add_asciistring(const TCHAR *string, int process=1); // For hardcoded 7bit/ASCII strings
int add_intstring(const int i); // returns offset in stringblock int add_intstring(const int i); // returns offset in stringblock
int preprocess_string(TCHAR *out, const TCHAR *in, WORD codepage=CP_ACP); int preprocess_string(TCHAR *out, const TCHAR *in, WORD codepage=CP_ACP);
void init_shellconstantvalues();
#ifdef NSIS_CONFIG_PLUGIN_SUPPORT #ifdef NSIS_CONFIG_PLUGIN_SUPPORT
int add_plugins_dir_initializer(void); int add_plugins_dir_initializer(void);
@ -328,10 +330,8 @@ class CEXEBuild {
* return a PS_ERROR. If this function call is overwriting a set user string, * return a PS_ERROR. If this function call is overwriting a set user string,
* this will return a PS_WARNING. * this will return a PS_WARNING.
*/ */
int SetLangString(TCHAR *name, LANGID lang, const TCHAR *str, BOOL unicode); int SetLangString(const TCHAR *name, LANGID lang, const TCHAR *str, BOOL LicenseData);
#ifndef _UNICODE int SetLangString(const TCHAR *name, LANGID lang, const TCHAR *str);
int SetUTF8LangString(TCHAR *name, LANGID lang, const char* stru8);
#endif
/** /**
* Sets the user string to the specific NLF_STRINGS id. * Sets the user string to the specific NLF_STRINGS id.
@ -405,6 +405,7 @@ class CEXEBuild {
bool no_space_texts; bool no_space_texts;
bool build_unicode;// generate installer with unicode exehead? bool build_unicode;// generate installer with unicode exehead?
bool build_lockedunicodetarget;
bool has_called_write_output; bool has_called_write_output;
@ -427,9 +428,6 @@ class CEXEBuild {
TCHAR build_output_filename[1024]; TCHAR build_output_filename[1024];
int build_include_depth; int build_include_depth;
#ifndef _UNICODE
bool build_include_isutf8; // UTF-8 LangString in .nsh hack for ANSI builds
#endif
// Added by ramon 6 jun 2003 // Added by ramon 6 jun 2003
#ifdef NSIS_SUPPORT_VERSION_INFO #ifdef NSIS_SUPPORT_VERSION_INFO
@ -465,7 +463,7 @@ class CEXEBuild {
GrowBuf build_instruction_entry_map,ubuild_instruction_entry_map, *cur_instruction_entry_map; GrowBuf build_instruction_entry_map,ubuild_instruction_entry_map, *cur_instruction_entry_map;
TinyGrowBuf build_functions, ubuild_functions, *cur_functions; TinyGrowBuf build_functions, ubuild_functions, *cur_functions;
TinyGrowBuf build_labels, ubuild_labels, *cur_labels; TinyGrowBuf build_labels, ubuild_labels, *cur_labels;
MLStringList build_strlist, ubuild_strlist, *cur_strlist; ExeHeadStringList build_strlist, ubuild_strlist, *cur_strlist;
GrowBuf build_langtables, ubuild_langtables, *cur_langtables; GrowBuf build_langtables, ubuild_langtables, *cur_langtables;
TinyGrowBuf build_pages, ubuild_pages, *cur_pages; TinyGrowBuf build_pages, ubuild_pages, *cur_pages;
TinyGrowBuf build_ctlcolors, ubuild_ctlcolors, *cur_ctlcolors; TinyGrowBuf build_ctlcolors, ubuild_ctlcolors, *cur_ctlcolors;

View file

@ -713,23 +713,23 @@ skipPage:
#define _RICHEDIT_VER 0x0200 #define _RICHEDIT_VER 0x0200
#include <richedit.h> #include <richedit.h>
#undef _RICHEDIT_VER #undef _RICHEDIT_VER
static DWORD dwRead; static DWORD g_cbLicRead;
DWORD CALLBACK StreamLicense(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) DWORD CALLBACK StreamLicense(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{ {
lstrcpyn((LPTSTR)pbBuff,(LPTSTR)(dwCookie+dwRead),cb/sizeof(TCHAR)); lstrcpyn((LPTSTR)pbBuff,(LPTSTR)(dwCookie+g_cbLicRead),cb/sizeof(TCHAR));
*pcb=lstrlen((LPTSTR)pbBuff)*sizeof(TCHAR); *pcb=lstrlen((LPTSTR)pbBuff)*sizeof(TCHAR);
dwRead+=*pcb; g_cbLicRead+=*pcb;
return 0; return 0;
} }
#ifdef _UNICODE #ifdef _UNICODE
// on-the-fly conversion of Unicode to ANSI (because Windows doesn'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_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) DWORD CALLBACK StreamLicenseRTF(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
{ {
size_t len = lstrlen(((LPWSTR) dwCookie)+dwRead); size_t len = lstrlen(((LPWSTR) dwCookie)+g_cbLicRead);
len = min(len, cb/sizeof(WCHAR)); len = min(len, cb/sizeof(WCHAR));
*pcb=WideCharToMultiByte(CP_ACP,0,((LPWSTR) dwCookie)+dwRead,len,(char*)pbBuff,cb,NULL,NULL); *pcb=WideCharToMultiByte(CP_ACP,0,((LPWSTR) dwCookie)+g_cbLicRead,len,(char*)pbBuff,cb,NULL,NULL);
// RTF uses only ASCII characters, so we can assume "number of output bytes" = "number of source WChar consumed" // RTF uses only ASCII characters, so we can assume "number of output bytes" = "number of source WChar consumed"
dwRead+=*pcb; g_cbLicRead+=*pcb;
return 0; return 0;
} }
#endif #endif
@ -738,7 +738,7 @@ static BOOL CALLBACK LicenseProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM
{ {
page *m_this_page=g_this_page; page *m_this_page=g_this_page;
HWND hwLicense; HWND hwLicense;
static int ignoreWMCommand; #define LicIgnoreWMCommand g_cbLicRead // g_cbLicRead is only used in WM_INITDIALOG during EM_STREAMIN
if (uMsg == WM_INITDIALOG) if (uMsg == WM_INITDIALOG)
{ {
@ -768,13 +768,13 @@ static BOOL CALLBACK LicenseProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM
SendMessage(hwLicense,EM_SETBKGNDCOLOR,0,lbg>=0?lbg:GetSysColor(-lbg)); SendMessage(hwLicense,EM_SETBKGNDCOLOR,0,lbg>=0?lbg:GetSysColor(-lbg));
#undef lbg #undef lbg
SendMessage(hwLicense,EM_SETEVENTMASK,0,ENM_LINK|ENM_KEYEVENTS); //XGE 8th September 2002 Or'd in ENM_KEYEVENTS SendMessage(hwLicense,EM_SETEVENTMASK,0,ENM_LINK|ENM_KEYEVENTS); //XGE 8th September 2002 Or'd in ENM_KEYEVENTS
dwRead=0;
SendMessage(hwLicense,EM_EXLIMITTEXT,0,mystrlen(l)); SendMessage(hwLicense,EM_EXLIMITTEXT,0,mystrlen(l));
g_cbLicRead = 0;
SendMessage(hwLicense,EM_STREAMIN,lt,(LPARAM)&es); SendMessage(hwLicense,EM_STREAMIN,lt,(LPARAM)&es);
ignoreWMCommand = 0; LicIgnoreWMCommand = 0;
return FALSE; return FALSE;
} }
if (uMsg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && !ignoreWMCommand) { if (uMsg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && !LicIgnoreWMCommand) {
if (m_this_page->flags & PF_LICENSE_FORCE_SELECTION) { if (m_this_page->flags & PF_LICENSE_FORCE_SELECTION) {
int is = SendMessage(GetUIItem(IDC_LICENSEAGREE), BM_GETCHECK, 0, 0) & BST_CHECKED; int is = SendMessage(GetUIItem(IDC_LICENSEAGREE), BM_GETCHECK, 0, 0) & BST_CHECKED;
m_this_page->flags &= ~PF_LICENSE_SELECTED; m_this_page->flags &= ~PF_LICENSE_SELECTED;
@ -836,7 +836,7 @@ static BOOL CALLBACK LicenseProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM
} }
if (uMsg == WM_NOTIFY_INIGO_MONTOYA) if (uMsg == WM_NOTIFY_INIGO_MONTOYA)
{ {
ignoreWMCommand++; LicIgnoreWMCommand++;
} }
return HandleStaticBkColor(); return HandleStaticBkColor();
} }

View file

@ -485,7 +485,7 @@ int CEXEBuild::DefineInnerLangString(int id, int process/*=-1*/) {
// @return If the language id, the variable name or string is invalid, it will // @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, // return a PS_ERROR. If this function call is overwriting a set user string,
// this will return a PS_WARNING. // this will return a PS_WARNING.
int CEXEBuild::SetLangString(TCHAR *name, LANGID lang, const TCHAR *str, BOOL unicode) { int CEXEBuild::SetLangString(const TCHAR *name, LANGID lang, const TCHAR *str, BOOL LicenseData) {
if (!str || !name) return PS_ERROR; if (!str || !name) return PS_ERROR;
LanguageTable *table = GetLangTable(lang); LanguageTable *table = GetLangTable(lang);
@ -493,33 +493,24 @@ int CEXEBuild::SetLangString(TCHAR *name, LANGID lang, const TCHAR *str, BOOL un
int sn; int sn;
if (_tcsclen(str) > NSIS_MAX_STRLEN-1) if (!LicenseData && _tcsclen(str) > NSIS_MAX_STRLEN-1)
warning_fl(_T("LangString \"%s\" longer than NSIS_MAX_STRLEN!"), name); warning_fl(_T("LangString \"%s\" longer than NSIS_MAX_STRLEN!"), name);
int pos = build_langstrings.get(name, &sn); int pos = build_langstrings.get(name, &sn);
if (pos < 0) if (pos < 0)
pos = build_langstrings.add(name, &sn); pos = build_langstrings.add(name, &sn);
if (table->lang_strings->set(sn, unicode ? str : CtoTString2(TtoCString(str),table->nlf.m_uCodePage))) if (table->lang_strings->set(sn, str))
return PS_WARNING; return PS_WARNING;
return PS_OK; return PS_OK;
} }
#ifndef _UNICODE int CEXEBuild::SetLangString(const TCHAR *name, LANGID lang, const TCHAR *str)
int CEXEBuild::SetUTF8LangString(TCHAR *name, LANGID lang, const char* stru8)
{ {
LanguageTable *table = GetLangTable(lang); return SetLangString(name, lang, str, false);
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. // Sets the user string to the specific NLF_STRINGS id.
// //
@ -912,41 +903,36 @@ void CEXEBuild::FillLanguageTable(LanguageTable *table) {
} }
} }
TCHAR SkipComments(FILE *f) { static UINT GetNextNLFLine(NStreamLineReader&lr, TCHAR*Buf, UINT cchBuf) {
int c; for (;;) {
while ((c = _fgettc(f))) { UINT err = lr.ReadLine(Buf, cchBuf);
while (c == _T('\n') || c == _T('\r')) { if (NStream::OK != err) {
c = _fgettc(f); // Skip empty lines if (lr.IsEOF()) err = NStream::OK;
return err;
} }
if (c == _T('#') || c == _T(';')) { if (NStream::IsNewline(*Buf, false)) continue;
while ((c = _fgettc(f))) { if (_T('#') != *Buf && _T(';') != *Buf) return err;
if (c == _T('\n')) break;
}
}
else break;
} }
return (TCHAR) c; }
static inline bool GetNextNLFLine(NStreamLineReader&lr, TCHAR*Buf, UINT cchBuf, UINT&errlr) {
errlr = GetNextNLFLine(lr, Buf, cchBuf);
return NStream::OK == errlr;
} }
// NSIS Language File parser // NSIS Language File parser
LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) { LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
BOOL unicode; NIStream strm;
FILE *f = FOPENTEXT2(filename, "r", &unicode); strm.StreamEncoding().SetCodepage(NStreamEncoding::ACP);
if (!f) { if (!strm.OpenFileForReading(filename)) {
ERROR_MSG(_T("Error: Can't open language file - \"%s\"!\n"),filename); ERROR_MSG(_T("Error: Can't open language file - \"%s\"!\n"),filename);
return 0; return 0;
} }
NStreamLineReader lr(strm);
#ifndef _UNICODE UINT errlr;
char fencoding = 0; // 0 = ansi, 8 = utf-8 (16/17 for uft-16le/be not supported)
if (IsUTF8BOM(f)) fencoding = 8;
#endif
// Check header // Check header
TCHAR buf[NSIS_MAX_STRLEN]; TCHAR buf[NSIS_MAX_STRLEN];
buf[0] = SkipComments(f); if (!GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN, errlr)) goto l_readerr;
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (_tcsncmp(buf, _T("NLF v"), 5)) { if (_tcsncmp(buf, _T("NLF v"), 5)) {
ERROR_MSG(_T("Error: Invalid language file.\n")); ERROR_MSG(_T("Error: Invalid language file.\n"));
return 0; return 0;
@ -960,31 +946,29 @@ LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
} }
// Get language ID // Get language ID
buf[0] = SkipComments(f); if (!GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN, errlr)) goto l_readerr;
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
LANGID lang_id = _ttoi(buf); LANGID lang_id = _ttoi(buf);
// Get appropriate table // Get appropriate table
LanguageTable *table = GetLangTable(lang_id); LanguageTable *table = GetLangTable(lang_id);
if (!table) if (!table) return 0;
return 0;
NLF *nlf = &table->nlf; NLF *nlf = &table->nlf;
if (nlf->m_bLoaded) { if (nlf->m_bLoaded) {
ERROR_MSG(_T("Error: can't load same language file twice.\n")); ERROR_MSG(_T("Error: can't load same language file twice.\n"));
return 0; return 0;
} }
// Generate language name // Generate language name
TCHAR *p, *p2, t = 0; TCHAR *p, *p2, *p3, t = 0;
p = _tcsrchr(filename, _T('.')); p = _tcsrchr(filename, _T('.'));
if (p) { if (p) {
t = *p; t = *p;
*p = 0; *p = 0;
} }
p2 = _tcsrchr(filename, _T('\\')); p2 = _tcsrchr(filename, _T('\\')), p3 = _tcsrchr(filename, _T('/'));
if (p3 > p2) p2 = p3;
if (p2) { if (p2) {
p2++; p2++;
nlf->m_szName = (TCHAR*)malloc((_tcslen(p2)+1)*sizeof(TCHAR)); nlf->m_szName = (TCHAR*)malloc((_tcslen(p2)+1)*sizeof(TCHAR));
@ -1006,8 +990,7 @@ LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
int temp; int temp;
// Get font // Get font
buf[0] = SkipComments(f); if (!GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN, errlr)) goto l_readerr;
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (!nlf->m_szFont) { if (!nlf->m_szFont) {
temp=_tcslen(buf); temp=_tcslen(buf);
while (buf[temp-1] == _T('\n') || buf[temp-1] == _T('\r')) { while (buf[temp-1] == _T('\n') || buf[temp-1] == _T('\r')) {
@ -1020,8 +1003,7 @@ LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
} }
} }
buf[0] = SkipComments(f); if (!GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN, errlr)) goto l_readerr;
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (!nlf->m_iFontSize) { if (!nlf->m_iFontSize) {
if (buf[0] != _T('-') || buf[1] != 0) { if (buf[0] != _T('-') || buf[1] != 0) {
nlf->m_iFontSize = _ttoi(buf); nlf->m_iFontSize = _ttoi(buf);
@ -1030,48 +1012,41 @@ LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
// Get code page // Get code page
nlf->m_uCodePage = CP_ACP; nlf->m_uCodePage = CP_ACP;
buf[0] = SkipComments(f); if (!GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN, errlr)) goto l_readerr;
_fgetts(buf+1, NSIS_MAX_STRLEN, f);
if (buf[0] != _T('-') || buf[1] != 0) { if (buf[0] != _T('-') || buf[1] != 0) {
nlf->m_uCodePage = _ttoi(buf); nlf->m_uCodePage = _ttoi(buf);
if (!IsValidCodePage(nlf->m_uCodePage)) if (!IsValidCodePage(nlf->m_uCodePage))
{
warning_fl(_T("%s language file uses a codepage that is not supported on this system, using ACP!"), nlf->m_szName);
nlf->m_uCodePage = CP_ACP; nlf->m_uCodePage = CP_ACP;
}
} }
#ifdef _UNICODE if (!lr.IsUnicode())
if (!unicode && nlf->m_szFont) // convert font name from ANSI to Unicode now that we know the language codepage
{ {
TCHAR* str = nlf->m_szFont; if (NStreamEncoding::IsUnicodeCodepage(nlf->m_uCodePage))
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")); warning_fl(_T("%s unicode language file does not have a BOM!"), nlf->m_szName);
return 0; }
if (nlf->m_szFont)
{
// Convert font name now that we know the codepage: ACP > NLF CP > TCHAR.
TCHAR* oldfont = nlf->m_szFont;
nlf->m_szFont = _tcsdup(CtoTString2(TtoCString(oldfont), nlf->m_uCodePage));
free(oldfont);
} }
free(str);
} }
#endif
// Get RTL setting // Get RTL setting
nlf->m_szStrings[NLF_RTL] = (TCHAR *)malloc(2*sizeof(TCHAR)); if (!GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN, errlr)) goto l_readerr;
nlf->m_szStrings[NLF_RTL] = (TCHAR*) malloc(2*sizeof(TCHAR));
nlf->m_bRTL = false; 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'))) { 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; nlf->m_bRTL = true;
_tcscpy(nlf->m_szStrings[NLF_RTL], _T("1"));
}
else {
_tcscpy(nlf->m_szStrings[NLF_RTL], _T("0"));
} }
_tcscpy(nlf->m_szStrings[NLF_RTL], nlf->m_bRTL ? _T("1") : _T("0"));
// Read strings // Read strings
for (int i = 0; i < NLF_STRINGS_NO_SPECIAL; i++) { for (int i = 0; i < NLF_STRINGS_NO_SPECIAL; i++) {
@ -1123,38 +1098,14 @@ LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
continue; continue;
} }
buf[0] = SkipComments(f); errlr = GetNextNLFLine(lr, buf, NSIS_MAX_STRLEN);
_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) { if (_tcslen(buf) == NSIS_MAX_STRLEN-1) {
ERROR_MSG(_T("Error: String too long (string #%d - \"%s\")\n"), i, NLFStrings[i].szLangStringName); ERROR_MSG(_T("Error: String too long (string #%d - \"%s\")\n"), i, NLFStrings[i].szLangStringName);
return 0; return 0;
} }
if (NStream::OK != errlr) goto l_readerr;
temp=_tcslen(buf); temp=_tcslen(buf);
while (buf[temp-1] == _T('\n') || buf[temp-1] == _T('\r')) { while (buf[temp-1] == _T('\n') || buf[temp-1] == _T('\r')) {
buf[--temp] = 0; buf[--temp] = 0;
} }
@ -1190,20 +1141,20 @@ LanguageTable * CEXEBuild::LoadLangFile(TCHAR *filename) {
else *out = *in; else *out = *in;
} }
*out = 0; *out = 0;
#ifdef _UNICODE if (!lr.IsUnicode())
if (!unicode)
{ {
TCHAR* str = nlf->m_szStrings[i]; TCHAR* oldstr = nlf->m_szStrings[i];
nlf->m_szStrings[i] = _tcsdup(CtoTString2(TtoCString(str),table->nlf.m_uCodePage)); nlf->m_szStrings[i] = _tcsdup(CtoTString2(TtoCString(oldstr),table->nlf.m_uCodePage));
free(str); free(oldstr);
} }
#endif
} }
fclose(f);
nlf->m_bLoaded = true; nlf->m_bLoaded = true;
return table; return table;
l_readerr:
ERROR_MSG(lr.GetErrorMessage(errlr).c_str());
return 0;
} }
void CEXEBuild::DeleteLangTable(LanguageTable *table) { void CEXEBuild::DeleteLangTable(LanguageTable *table) {

View file

@ -150,6 +150,7 @@ static void print_usage()
_T(" ") OPT_STR _T("PAUSE pauses after execution\n") _T(" ") OPT_STR _T("PAUSE pauses after execution\n")
_T(" ") OPT_STR _T("NOCONFIG disables inclusion of <path to makensis.exe>") PLATFORM_PATH_SEPARATOR_STR _T("nsisconf.nsh\n") _T(" ") OPT_STR _T("NOCONFIG disables inclusion of <path to makensis.exe>") PLATFORM_PATH_SEPARATOR_STR _T("nsisconf.nsh\n")
_T(" ") OPT_STR _T("NOCD disabled the current directory change to that of the .nsi file\n") _T(" ") OPT_STR _T("NOCD disabled the current directory change to that of the .nsi file\n")
_T(" ") OPT_STR _T("INPUTCHARSET <") TSTR_INPUTCHARSET _T(">\n")
_T(" ") OPT_STR _T("Ddefine[=value] defines the symbol \"define\" for the script [to value]\n") _T(" ") OPT_STR _T("Ddefine[=value] defines the symbol \"define\" for the script [to value]\n")
_T(" ") OPT_STR _T("Xscriptcmd executes scriptcmd in script (i.e. \"") OPT_STR _T("XOutFile poop.exe\")\n") _T(" ") OPT_STR _T("Xscriptcmd executes scriptcmd in script (i.e. \"") OPT_STR _T("XOutFile poop.exe\")\n")
_T(" ") _T(" parameters are processed by order (") OPT_STR _T("Ddef ins.nsi != ins.nsi ") OPT_STR _T("Ddef)\n") _T(" ") _T(" parameters are processed by order (") OPT_STR _T("Ddef ins.nsi != ins.nsi ") OPT_STR _T("Ddef)\n")
@ -201,13 +202,11 @@ static tstring get_home()
static int process_config(CEXEBuild& build, tstring& conf) static int process_config(CEXEBuild& build, tstring& conf)
{ {
BOOL unicode; NIStream strm;
FILE *cfg=FOPENTEXT2(conf.c_str(),"rt",&unicode); if (strm.OpenFileForReading(conf.c_str()))
if (cfg)
{ {
build.INFO_MSG(_T("Processing config: %s\n"), conf.c_str()); build.INFO_MSG(_T("Processing config: %s\n"),conf.c_str());
int ret=build.process_script(cfg,(TCHAR*)conf.c_str(),unicode); int ret=build.process_script(strm,conf.c_str());
fclose(cfg);
if (ret != PS_OK && ret != PS_EOF) if (ret != PS_OK && ret != PS_EOF)
{ {
build.ERROR_MSG(_T("Error in config on line %d -- aborting creation process\n"),build.linecnt); build.ERROR_MSG(_T("Error in config on line %d -- aborting creation process\n"),build.linecnt);
@ -234,6 +233,16 @@ static int change_to_script_dir(CEXEBuild& build, tstring& script)
return 0; return 0;
} }
static inline bool HasReqParam(TCHAR**argv,int argi,int argc)
{
if (argi>=argc || !*argv[argi])
{
PrintColorFmtMsg_ERR(_T("Error: Missing required parameter!\n"));
return false;
}
return true;
}
#ifdef NSIS_HPUX_ALLOW_UNALIGNED_DATA_ACCESS #ifdef NSIS_HPUX_ALLOW_UNALIGNED_DATA_ACCESS
extern "C" void allow_unaligned_data_access(); extern "C" void allow_unaligned_data_access();
#endif #endif
@ -247,16 +256,16 @@ int _tmain(int argc, TCHAR **argv)
#endif #endif
CEXEBuild build; CEXEBuild build;
int do_cd=1; NStreamEncoding inputenc;
int outputtried=0; bool outputtried=0;
int argpos=1; bool in_files=0;
bool do_cd=1;
bool no_logo=0;
int nousage=0; int nousage=0;
int argpos=1;
int tmpargpos=1;
int files_processed=0; int files_processed=0;
int cmds_processed=0; int cmds_processed=0;
FILE *fp;
int tmpargpos=1;
int no_logo=0;
int in_files=0;
#ifdef _UNICODE #ifdef _UNICODE
#ifndef _O_U8TEXT #ifndef _O_U8TEXT
@ -329,7 +338,7 @@ int _tmain(int argc, TCHAR **argv)
} }
else if (S7IsChEqualI('x',argv[argpos][1]) && argv[argpos][2]) else if (S7IsChEqualI('x',argv[argpos][1]) && argv[argpos][2])
{ {
if (build.process_oneline(argv[argpos]+2,_T("command line"),argpos+1) != PS_OK) if (build.process_oneline(argv[argpos]+2,_T("<command line>"),argpos+1) != PS_OK)
{ {
return 1; return 1;
} }
@ -379,12 +388,12 @@ int _tmain(int argc, TCHAR **argv)
} }
else if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND"))) else if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND")))
{ {
if (!HasReqParam(argv, ++argpos, argc)) break;
#ifdef _WIN32 #ifdef _WIN32
build.notify_hwnd=(HWND)_ttol(argv[++argpos]); build.notify_hwnd=(HWND)_ttol(argv[argpos]);
if (!IsWindow(build.notify_hwnd)) if (!IsWindow(build.notify_hwnd))
build.notify_hwnd=0; build.notify_hwnd=0;
#else #else
argpos++;
build.warning(OPT_STR _T("NOTIFYHWND is disabled for non Win32 platforms.")); build.warning(OPT_STR _T("NOTIFYHWND is disabled for non Win32 platforms."));
#endif #endif
} }
@ -426,7 +435,20 @@ int _tmain(int argc, TCHAR **argv)
build.warning(OPT_STR _T("Px is disabled for non Win32 platforms.")); build.warning(OPT_STR _T("Px is disabled for non Win32 platforms."));
#endif #endif
} }
else break; else if (!_tcsicmp(&argv[argpos][1],_T("INPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("ICS")))
{
if (!HasReqParam(argv, ++argpos, argc)) break;
WORD cp = GetEncodingFromString(argv[argpos]);
if (NStreamEncoding::UNKNOWN == cp)
{
if (_tcsicmp(argv[argpos], _T("AUTO")))
build.warning(OPT_STR _T("INPUTCHARSET: Ignoring invalid charset %s"), argv[argpos]);
cp = NStreamEncoding::AUTO;
}
inputenc.SetCodepage(cp);
}
else
break;
} }
else else
{ {
@ -465,45 +487,43 @@ int _tmain(int argc, TCHAR **argv)
} }
{ {
TCHAR sfile[1024]; tstring nsifile;
BOOL unicode=FALSE; NIStream strm;
if (!_tcscmp(argv[argpos],_T("-")) && !in_files) if (!_tcscmp(argv[argpos],_T("-")) && !in_files)
{ {
fp=stdin; strm.OpenStdIn(inputenc);
_tcscpy(sfile,_T("stdin")); nsifile = _T("<stdin>");
} }
else else
{ {
_tcscpy(sfile,argv[argpos]); nsifile = argv[argpos];
fp=FOPENTEXT2(sfile,"rt",&unicode); if (!strm.OpenFileForReading(nsifile.c_str(),inputenc))
if (!fp)
{ {
_stprintf(sfile,_T("%s.nsi"),argv[argpos]); nsifile += _T(".nsi");
fp=FOPENTEXT2(sfile,"rt",&unicode); if (!strm.OpenFileForReading(nsifile.c_str(),inputenc))
if (!fp)
{ {
sfile[_tcslen(sfile)-4]=0; nsifile = argv[argpos];
build.ERROR_MSG(_T("Can't open script \"%s\"\n"),sfile); build.ERROR_MSG(_T("Can't open script \"%s\"\n"),nsifile.c_str());
return 1; return 1;
} }
} }
if (do_cd) if (do_cd)
{ {
tstring script_file = tstring(sfile); if (change_to_script_dir(build, nsifile))
if (change_to_script_dir(build, script_file))
return 1; return 1;
} }
build.set_default_output_filename(remove_file_extension(sfile)+_T(".exe")); build.set_default_output_filename(remove_file_extension(nsifile)+_T(".exe"));
} }
build.notify(MAKENSIS_NOTIFY_SCRIPT,sfile); build.notify(MAKENSIS_NOTIFY_SCRIPT,nsifile.c_str());
build.INFO_MSG(_T("Processing script file: \"%s\"\n"),sfile); TCHAR bufcpdisp[20];
int ret=build.process_script(fp,sfile,unicode); strm.StreamEncoding().GetCPDisplayName(bufcpdisp);
if (fp != stdin) fclose(fp); build.INFO_MSG(_T("Processing script file: \"%s\" (%s)\n"),nsifile.c_str(),bufcpdisp);
int ret=build.process_script(strm,nsifile.c_str());
if (ret != PS_EOF && ret != PS_OK) if (ret != PS_EOF && ret != PS_OK)
{ {
build.ERROR_MSG(_T("Error in script \"%s\" on line %d -- aborting creation process\n"),sfile,build.linecnt); build.ERROR_MSG(_T("Error in script \"%s\" on line %d -- aborting creation process\n"),nsifile.c_str(),build.linecnt);
return 1; return 1;
} }
} }
@ -534,5 +554,5 @@ int _tmain(int argc, TCHAR **argv)
build.ERROR_MSG(_T("Error - aborting creation process\n")); build.ERROR_MSG(_T("Error - aborting creation process\n"));
return 1; return 1;
} }
return 0; return 0;
} }

View file

@ -225,12 +225,12 @@ void CEXEBuild::del_date_time_predefines()
} }
#endif #endif
int CEXEBuild::process_script(FILE *filepointer, const TCHAR *filename, BOOL unicode) int CEXEBuild::process_script(NIStream&Strm, const TCHAR *filename)
{ {
linecnt = 0; NStreamLineReader linereader(Strm);
fp = filepointer; curlinereader = &linereader;
curfilename = filename; curfilename = filename;
curfile_unicode = unicode; linecnt = 0;
if (has_called_write_output) if (has_called_write_output)
{ {
@ -254,9 +254,8 @@ int CEXEBuild::process_script(FILE *filepointer, const TCHAR *filename, BOOL uni
del_date_time_predefines(); del_date_time_predefines();
#endif #endif
fp = 0; curlinereader = 0;
curfilename = 0; curfilename = 0;
curfile_unicode = FALSE;
if (m_linebuild.getlen()) if (m_linebuild.getlen())
{ {
@ -770,17 +769,29 @@ void CEXEBuild::ps_addtoline(const TCHAR *str, GrowBuf &linedata, StringList &hi
int CEXEBuild::parseScript() int CEXEBuild::parseScript()
{ {
assert(curlinereader);
TCHAR str[MAX_LINELENGTH]; TCHAR str[MAX_LINELENGTH];
NStreamLineReader &linereader = *curlinereader;
for (;;) for (;;)
{ {
TCHAR *p=str; UINT lrres = linereader.ReadLine(str,MAX_LINELENGTH);
*p=0;
_fgetts(str,MAX_LINELENGTH,fp);
linecnt++; linecnt++;
if (feof(fp)&&!str[0]) break; if (NStream::OK != lrres)
{
if (linereader.IsEOF())
{
if(!str[0]) break;
}
else
{
ERROR_MSG(linereader.GetErrorMessage(lrres,curfilename,linecnt).c_str());
return PS_ERROR;
}
}
// remove trailing whitespace // remove trailing whitespace
TCHAR *p = str;
while (*p) p++; while (*p) p++;
if (p > str) p--; if (p > str) p--;
while (p >= str && (*p == _T('\r') || *p == _T('\n') || *p == _T(' ') || *p == _T('\t'))) p--; while (p >= str && (*p == _T('\r') || *p == _T('\n') || *p == _T(' ') || *p == _T('\t'))) p--;
@ -809,39 +820,33 @@ int CEXEBuild::parseScript()
return PS_EOF; return PS_EOF;
} }
int CEXEBuild::includeScript(TCHAR *f) int CEXEBuild::includeScript(const TCHAR *f, NStreamEncoding&enc)
{ {
SCRIPT_MSG(_T("!include: \"%s\"\n"),f); NIStream incstrm;
BOOL unicode; const bool openok = incstrm.OpenFileForReading(f,enc);
FILE *incfp=FOPENTEXT2(f,"rt",&unicode); TCHAR bufcpdisp[20];
if (!incfp) incstrm.StreamEncoding().GetCPDisplayName(bufcpdisp);
SCRIPT_MSG(_T("!include: \"%s\" (%s)\n"),f,bufcpdisp);
if (!openok)
{ {
ERROR_MSG(_T("!include: could not open file: \"%s\"\n"),f); ERROR_MSG(_T("!include: could not open file: \"%s\"\n"),f);
return PS_ERROR; return PS_ERROR;
} }
// auto-fclose(3) incfp
MANAGE_WITH(incfp, fclose);
if (build_include_depth >= MAX_INCLUDEDEPTH) if (build_include_depth >= MAX_INCLUDEDEPTH)
{ {
ERROR_MSG(_T("parseScript: too many levels of includes (%d max).\n"),MAX_INCLUDEDEPTH); ERROR_MSG(_T("!include: too many levels of includes (%d max).\n"),MAX_INCLUDEDEPTH);
return PS_ERROR; return PS_ERROR;
} }
build_include_depth++; build_include_depth++;
#ifndef _UNICODE
const bool org_build_include_isutf8 = build_include_isutf8;
build_include_isutf8 = IsUTF8BOM(incfp);
#endif
int last_linecnt=linecnt; const int last_linecnt=linecnt;
linecnt=0; linecnt=0;
const TCHAR *last_filename=curfilename; const TCHAR *last_filename=curfilename;
BOOL last_unicode=curfile_unicode;
curfilename=f; curfilename=f;
curfile_unicode=unicode; NStreamLineReader linereader(incstrm);
FILE *last_fp=fp; NStreamLineReader*last_linereader=curlinereader;
fp=incfp; curlinereader=&linereader;
#ifdef NSIS_SUPPORT_STANDARD_PREDEFINES #ifdef NSIS_SUPPORT_STANDARD_PREDEFINES
// Added by Sunil Kamath 11 June 2003 // Added by Sunil Kamath 11 June 2003
@ -857,16 +862,10 @@ int CEXEBuild::includeScript(TCHAR *f)
restore_timestamp_predefine(oldtimestamp); restore_timestamp_predefine(oldtimestamp);
#endif #endif
#ifndef _UNICODE const int errlinecnt=linecnt;
build_include_isutf8 = org_build_include_isutf8;
#endif
int errlinecnt=linecnt;
linecnt=last_linecnt; linecnt=last_linecnt;
curfilename=last_filename; curfilename=last_filename;
curfile_unicode=last_unicode; curlinereader = last_linereader;
fp=last_fp;
build_include_depth--; build_include_depth--;
if (r != PS_EOF && r != PS_OK) if (r != PS_EOF && r != PS_OK)
@ -904,52 +903,75 @@ int CEXEBuild::MacroExists(const TCHAR *macroname)
return 0; return 0;
} }
int CEXEBuild::LoadLicenseFile(TCHAR *file, TCHAR** pdata, LineParser &line, BOOL* unicode) // caller must free *pdata, even on error result int CEXEBuild::LoadLicenseFile(const TCHAR *file, TCHAR** pdata, const TCHAR *cmdname, WORD AnsiCP) // caller must free *pdata, even on error result
{ {
FILE *fp=FOPENTEXT2(file,"rt",unicode); NIStream strm;
if (!fp) if (!strm.OpenFileForReading(file))
{ {
ERROR_MSG(_T("%s: open failed \"%s\"\n"),line.gettoken_str(0),file); ERROR_MSG(_T("%s: open failed \"%s\"\n"),cmdname,file);
PRINTHELP() print_help(cmdname);
}
MANAGE_WITH(fp, fclose);
unsigned int beginning=ftell(fp); // (we might be positionned after a BOM)
fseek(fp,0,SEEK_END);
unsigned int datalen=ftell(fp)-beginning; // size of file in bytes! not a number of characters
if (!datalen)
{
ERROR_MSG(_T("%s: empty license file \"%s\"\n"),line.gettoken_str(0),file);
return PS_ERROR; return PS_ERROR;
} }
fseek(fp,beginning,SEEK_SET); FILE *f=strm.GetHandle();
TCHAR *data=(TCHAR*)malloc((datalen+2)*sizeof(TCHAR)); // alloc enough for worst-case scenario (ANSI/UTF8 characters read in WCHARs) UINT cbBOMOffset=ftell(f); // We might be positioned after a BOM
fseek(f,0,SEEK_END);
UINT cbFileData=ftell(f)-cbBOMOffset; // Size of file in bytes!
if (!cbFileData)
{
warning_fl(_T("%s: empty license file \"%s\"\n"),cmdname,file);
}
else
build_lockedunicodetarget=true;
fseek(f,cbBOMOffset,SEEK_SET);
UINT cbTotalData=sizeof(TCHAR)+cbFileData+sizeof(TCHAR); // SF_*+file+\0
TCHAR*data=(TCHAR*)malloc(cbTotalData);
if (!data) if (!data)
{ {
ERROR_MSG(_T("Internal compiler error #12345: %s malloc(%d) failed.\n"),line.gettoken_str(0),(datalen+2)*sizeof(TCHAR)); l_OOM:
ERROR_MSG(_T("Internal compiler error #12345: %s malloc(%d) failed.\n"),cmdname,cbTotalData);
return PS_ERROR; return PS_ERROR;
} }
*pdata = data; // memory will be released by caller *pdata=data; // memory will be released by caller
TCHAR *ldata=data+1; *((TCHAR*)((char*)data+cbTotalData-sizeof(TCHAR)))=_T('\0');
while (_fgetts(ldata, data+datalen+2-ldata, fp)) // _fgetts translates ANSI/UTF8 characters to TCHAR //BUGBUG: There is no reason to store ASCII files as TCHAR
ldata += _tcslen(ldata); TCHAR*ldata=data+1;
if (ferror(fp)) if (!strm.ReadOctets(ldata,&cbFileData))
{ {
ERROR_MSG(_T("%s: can't read file.\n"),line.gettoken_str(0)); ERROR_MSG(_T("%s: can't read file.\n"),cmdname);
return PS_ERROR; return PS_ERROR;
} }
bool disallowrichedunicode = false; // We have to convert the content of the license file to wchar_t
#ifdef _UNICODE const WORD srccp=strm.StreamEncoding().IsUnicode() ? strm.StreamEncoding().GetCodepage() : AnsiCP;
if (!build_unicode) const UINT cbcu=NStreamEncoding::GetCodeUnitSize(srccp);
disallowrichedunicode = true; //RichEdit 1.0 does not support unicode if (sizeof(TCHAR) < cbcu)
{
ERROR_MSG(_T("%s: wchar_t conversion failed!\n"),cmdname);
return PS_ERROR;
}
// Create a fake character in the "header" part of the buffer
char*lichdr=((char*)ldata) - cbcu;
*((char*)lichdr)='X';
if (cbcu > 1) *((WORD*)lichdr)='X';
//BUGBUG: No room: if (cbcu > 2) *((UINT32*)lichdr)='X';
wchar_t*wcdata=DupWCFromBytes(lichdr,cbcu+cbFileData,srccp);
if ((wchar_t*)-1==wcdata)
{
ERROR_MSG(_T("%s: wchar_t conversion failed!\n"),cmdname);
return PS_ERROR;
}
free(data);
*pdata=data=wcdata;
ldata=data+1;
if (!data) goto l_OOM;
const bool isRTF=!memcmp(ldata,_T("{\\rtf"),5*sizeof(TCHAR));
if (isRTF)
*data=SF_RTF;
else else
*unicode = true; // _fgetts converted to TCHAR *data=build_unicode ? (SF_TEXT|SF_UNICODE) : (SF_TEXT);
#endif
if (!memcmp(data+1,_T("{\\rtf"),5*sizeof(TCHAR)))
*data = SF_RTF;
else if (*unicode && !disallowrichedunicode)
*data = SF_TEXT|SF_UNICODE;
else
*data = SF_TEXT;
return PS_OK; return PS_OK;
} }
@ -968,7 +990,7 @@ int CEXEBuild::process_oneline(TCHAR *line, const TCHAR *filename, int linenum)
TCHAR *oldfilename = NULL; TCHAR *oldfilename = NULL;
TCHAR *oldtimestamp = NULL; TCHAR *oldtimestamp = NULL;
TCHAR *oldline = NULL; TCHAR *oldline = NULL;
BOOL is_commandline = !_tcscmp(filename,_T("command line")); BOOL is_commandline = !_tcscmp(filename,_T("<command line>"));
BOOL is_macro = !_tcsncmp(filename,_T("macro:"),_tcslen(_T("macro:"))); BOOL is_macro = !_tcsncmp(filename,_T("macro:"),_tcslen(_T("macro:")));
if(!is_commandline) { // Don't set the predefines for command line /X option if(!is_commandline) { // Don't set the predefines for command line /X option
@ -1047,7 +1069,6 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
#endif #endif
multiple_entries_instruction=0; multiple_entries_instruction=0;
entry ent={0,}; entry ent={0,};
switch (which_token) switch (which_token)
{ {
@ -1105,14 +1126,24 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
{ {
TCHAR str[MAX_LINELENGTH]; TCHAR str[MAX_LINELENGTH];
TCHAR *p=str; TCHAR *p=str;
str[0]=0; UINT lrres = curlinereader->ReadLine(str,MAX_LINELENGTH);
_fgetts(str,MAX_LINELENGTH,fp); if (NStream::OK != lrres)
//SCRIPT_MSG(_T("%s%s"), str, str[_tcslen(str)-1]==_T('\n')?_T(""):_T("\n"));
if (feof(fp) && !str[0])
{ {
ERROR_MSG(_T("!macro \"%s\": unterminated (no !macroend found in file)!\n"),line.gettoken_str(1)); if (curlinereader->IsEOF())
return PS_ERROR; {
if (!str[0])
{
ERROR_MSG(_T("!macro \"%s\": unterminated (no !macroend found in file)!\n"),line.gettoken_str(1));
return PS_ERROR;
}
}
else
{
ERROR_MSG(curlinereader->GetErrorMessage(lrres).c_str());
return PS_ERROR;
}
} }
//SCRIPT_MSG(_T("%s%s"), str, str[_tcslen(str)-1]==_T('\n')?_T(""):_T("\n"));
// remove trailing whitespace // remove trailing whitespace
while (*p) p++; while (*p) p++;
if (p > str) p--; if (p > str) p--;
@ -1742,21 +1773,13 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
TCHAR *name = line.gettoken_str(1); TCHAR *name = line.gettoken_str(1);
LANGID lang = line.gettoken_int(2); LANGID lang = line.gettoken_int(2);
TCHAR *str = line.gettoken_str(3); TCHAR *str = line.gettoken_str(3);
int ret; const int ret = SetLangString(name, lang, str);
#ifndef _UNICODE
if (build_include_isutf8)
ret = SetUTF8LangString(name, lang, str);
else
#endif
ret = SetLangString(name, lang, str, curfile_unicode);
if (ret == PS_WARNING) if (ret == PS_WARNING)
warning_fl(_T("LangString \"%s\" set multiple times for %d, wasting space"), name, lang); warning_fl(_T("LangString \"%s\" set multiple times for %d, wasting space"), name, lang);
else if (ret == PS_ERROR) { else if (ret == PS_ERROR) {
ERROR_MSG(_T("Error: can't set LangString \"%s\"!\n"), name); ERROR_MSG(_T("Error: can't set LangString \"%s\"!\n"), name);
return PS_ERROR; return PS_ERROR;
} }
// BUGBUG: Does not display UTF-8 properly.
SCRIPT_MSG(_T("LangString: \"%s\" %d \"%s\"\n"), name, lang, str); SCRIPT_MSG(_T("LangString: \"%s\" %d \"%s\"\n"), name, lang, str);
} }
return PS_OK; return PS_OK;
@ -1765,10 +1788,11 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
return PS_ERROR; return PS_ERROR;
case TOK_LICENSELANGSTRING: case TOK_LICENSELANGSTRING:
{ {
const TCHAR *cmdnam = get_commandtoken_name(which_token);
#ifdef NSIS_CONFIG_SILENT_SUPPORT #ifdef NSIS_CONFIG_SILENT_SUPPORT
if (build_header.flags&(CH_FLAGS_SILENT|CH_FLAGS_SILENT_LOG)) if (build_header.flags&(CH_FLAGS_SILENT|CH_FLAGS_SILENT_LOG))
{ {
warning_fl(_T("LicenseLangString: SilentInstall enabled, wasting space")); warning_fl(_T("%s: SilentInstall enabled, wasting space"), cmdnam);
} }
#endif #endif
TCHAR *name = line.gettoken_str(1); TCHAR *name = line.gettoken_str(1);
@ -1777,22 +1801,26 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
TCHAR *data = NULL; TCHAR *data = NULL;
MANAGE_WITH(data, free); MANAGE_WITH(data, free);
BOOL unicode;
int ret = LoadLicenseFile(file, &data, line, &unicode); WORD AnsiCP = CP_ACP;
LanguageTable *pLT = GetLangTable(lang);
if (pLT) AnsiCP = pLT->nlf.m_uCodePage;
int ret = LoadLicenseFile(file, &data, cmdnam, AnsiCP);
if (ret != PS_OK) if (ret != PS_OK)
return ret; return ret;
ret = SetLangString(name, lang, data, unicode); ret = SetLangString(name, lang, data, true);
if (ret == PS_WARNING) if (ret == PS_WARNING)
warning_fl(_T("LicenseLangString \"%s\" set multiple times for %d, wasting space"), name, lang); warning_fl(_T("%s \"%s\" set multiple times for %d, wasting space"), cmdnam, name, lang);
else if (ret == PS_ERROR) else if (ret == PS_ERROR)
{ {
ERROR_MSG(_T("Error: can't set LicenseLangString \"%s\"!\n"), name); ERROR_MSG(_T("Error: can't set %s \"%s\"!\n"), cmdnam, name);
return PS_ERROR; return PS_ERROR;
} }
SCRIPT_MSG(_T("LicenseLangString: \"%s\" %d \"%s\"\n"), name, lang, file); SCRIPT_MSG(_T("%s: \"%s\" %d \"%s\"\n"), cmdnam, name, lang, file);
return PS_ERROR;
} }
return PS_OK; return PS_OK;
case TOK_NAME: case TOK_NAME:
@ -2033,11 +2061,13 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
return PS_OK; return PS_OK;
case TOK_LICENSEDATA: case TOK_LICENSEDATA:
{ {
const TCHAR *cmdnam = get_commandtoken_name(which_token);
int idx = 0; int idx = 0;
TCHAR *file = line.gettoken_str(1); TCHAR *file = line.gettoken_str(1);
TCHAR *data = NULL; TCHAR *data = NULL;
TCHAR *filedata = NULL; TCHAR *filedata = NULL;
MANAGE_WITH(filedata, free); MANAGE_WITH(filedata, free);
WORD cp = CP_ACP;
if (file[0] == _T('$') && file[1] == _T('(')) if (file[0] == _T('$') && file[1] == _T('('))
{ {
@ -2053,24 +2083,22 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
if (!idx) if (!idx)
{ {
BOOL unicode; int ret = LoadLicenseFile(file, &filedata, cmdnam, cp);
int ret = LoadLicenseFile(file, &filedata, line, &unicode);
if (ret != PS_OK) if (ret != PS_OK)
return ret; return ret;
data = filedata; data = filedata;
} }
if (!cur_page) { if (!cur_page) {
if (SetInnerString(NLF_LICENSE_DATA,data) == PS_WARNING) if (SetInnerString(NLF_LICENSE_DATA,data) == PS_WARNING)
warning_fl(_T("%s: specified multiple times, wasting space"),line.gettoken_str(0)); warning_fl(_T("%s: specified multiple times, wasting space"),cmdnam);
} }
else { else {
if (cur_page_type != PAGE_LICENSE) { if (cur_page_type != PAGE_LICENSE) {
ERROR_MSG(_T("Error: LicenseData can only be used inside PageEx license.\n")); ERROR_MSG(_T("Error: LicenseData can only be used inside PageEx license.\n"));
return PS_ERROR; return PS_ERROR;
} }
cur_page->parms[1] = add_string(data, false, cp);
cur_page->parms[1] = add_string(data, 0);
} }
SCRIPT_MSG(_T("LicenseData: \"%s\"\n"),file); SCRIPT_MSG(_T("LicenseData: \"%s\"\n"),file);
@ -2791,17 +2819,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
LANGID lang_id = _ttoi(line.gettoken_str(1) + 6); LANGID lang_id = _ttoi(line.gettoken_str(1) + 6);
LanguageTable *table = GetLangTable(lang_id); LanguageTable *table = GetLangTable(lang_id);
const TCHAR*facename = line.gettoken_str(2); const TCHAR*facename = line.gettoken_str(2);
#ifndef _UNICODE table->nlf.m_szFont = _tcsdup(facename);
if (build_include_isutf8)
{
EXEHEADTCHAR_T *bufEHTStr = UTF8ToExeHeadTStrDup(facename, table->nlf.m_uCodePage);
table->nlf.m_szFont = bufEHTStr;
}
else
#endif
{
table->nlf.m_szFont = _tcsdup(facename);
}
table->nlf.m_iFontSize = line.gettoken_int(3); table->nlf.m_iFontSize = line.gettoken_int(3);
if (table->nlf.m_szFont) if (table->nlf.m_szFont)
@ -2812,19 +2830,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
else else
{ {
const TCHAR*facename = line.gettoken_str(1); const TCHAR*facename = line.gettoken_str(1);
#ifndef _UNICODE _tcsnccpy(build_font, facename, COUNTOF(build_font));
if (build_include_isutf8)
{
EXEHEADTCHAR_T *bufEHTStr = UTF8ToExeHeadTStrDup(facename, CP_ACP);
if (!bufEHTStr) ++failed;
_tcsnccpy(build_font, bufEHTStr, COUNTOF(build_font));
free(bufEHTStr);
}
else
#endif
{
_tcsnccpy(build_font, facename, COUNTOF(build_font));
}
build_font_size = line.gettoken_int(2); build_font_size = line.gettoken_int(2);
if (!failed) SCRIPT_MSG(_T("SetFont: \"%s\" %s\n"), facename, line.gettoken_str(2)); if (!failed) SCRIPT_MSG(_T("SetFont: \"%s\" %s\n"), facename, line.gettoken_str(2));
@ -2893,15 +2899,19 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
#ifdef _UNICODE #ifdef _UNICODE
case TOK_TARGETUNICODE: case TOK_TARGETUNICODE:
{ {
if (build_compressor_set)
{
ERROR_MSG(_T("Error: Can't change target charset after data already got compressed or header already changed!\n"));
return PS_ERROR;
}
int k = line.gettoken_enum(1,_T("false\0true\0")); int k = line.gettoken_enum(1,_T("false\0true\0"));
if (-1==k) PRINTHELP(); if (-1==k) PRINTHELP();
SCRIPT_MSG(_T("Unicode: %s\n"),k?_T("true"):_T("false")); SCRIPT_MSG(_T("Unicode: %s\n"),k?_T("true"):_T("false"));
if (set_target_charset(!!k) != PS_OK) const bool newtargetcs = !!k;
if (newtargetcs != build_unicode)
{
if (build_compressor_set || build_lockedunicodetarget)
{
ERROR_MSG(_T("Error: Can't change target charset after data already got compressed or header already changed!\n"));
return PS_ERROR;
}
}
if (set_target_charset(newtargetcs) != PS_OK)
{ {
ERROR_MSG(_T("Error: Unable to set target charset (adequate stub not found?)\n")); ERROR_MSG(_T("Error: Unable to set target charset (adequate stub not found?)\n"));
return PS_ERROR; return PS_ERROR;
@ -3035,7 +3045,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
{ {
line.eattoken(); line.eattoken();
define=line.gettoken_str(1); define=line.gettoken_str(1);
if (dupemode==1 && definedlist.find(define))return PS_OK; if (dupemode==1 && definedlist.find(define)) return PS_OK;
} }
@ -3242,19 +3252,26 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
case TOK_P_INCLUDE: case TOK_P_INCLUDE:
{ {
bool required = true; bool required = true;
NStreamEncoding enc(NStreamEncoding::AUTO);
TCHAR *f = line.gettoken_str(1); TCHAR *f;
unsigned int toks = line.getnumtokens() - 1;
if(!_tcsicmp(f,_T("/nonfatal"))) { for(unsigned int tok = 0; toks;)
if (line.getnumtokens()!=3) {
PRINTHELP(); f = line.gettoken_str(++tok);
if (tok >= toks) break;
f = line.gettoken_str(2); if(!_tcsicmp(f,_T("/nonfatal"))) {
required = false; required = false;
} else if (line.getnumtokens()!=2) { }
PRINTHELP(); TCHAR buf[9+1];
my_strncpy(buf,f,COUNTOF(buf));
if(!_tcsicmp(buf,_T("/charset="))) {
WORD cp = GetEncodingFromString(f+9);
if (NStreamEncoding::UNKNOWN == cp) toks = 0;
enc.SafeSetCodepage(cp);
}
} }
if (!toks || !*f) PRINTHELP();
TCHAR *fc = my_convert(f); TCHAR *fc = my_convert(f);
int included = 0; int included = 0;
@ -3282,7 +3299,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
tstring incfile = basedir + *files_itr; tstring incfile = basedir + *files_itr;
if (includeScript((TCHAR *) incfile.c_str()) != PS_OK) { if (includeScript(incfile.c_str(), enc) != PS_OK) {
return PS_ERROR; return PS_ERROR;
} }
@ -3311,7 +3328,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
tstring incfile = tstring(incdir) + PLATFORM_PATH_SEPARATOR_STR + basedir + *incdir_itr; tstring incfile = tstring(incdir) + PLATFORM_PATH_SEPARATOR_STR + basedir + *incdir_itr;
if (includeScript((TCHAR *) incfile.c_str()) != PS_OK) { if (includeScript(incfile.c_str(), enc) != PS_OK) {
return PS_ERROR; return PS_ERROR;
} }
@ -3327,13 +3344,13 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
if (!included) if (!included)
{ {
if(required) { if(required) {
ERROR_MSG(_T("!include: could not find: \"%s\"\n"),f); ERROR_MSG(_T("!include: could not find: \"%s\"\n"),f);
return PS_ERROR; return PS_ERROR;
} else { } else {
warning_fl(_T("!include: could not find: \"%s\""),f); warning_fl(_T("!include: could not find: \"%s\""),f);
}
} }
} }
}
return PS_OK; return PS_OK;
case TOK_P_CD: case TOK_P_CD:
if (!line.gettoken_str(1)[0] || _tchdir(line.gettoken_str(1))) if (!line.gettoken_str(1)[0] || _tchdir(line.gettoken_str(1)))
@ -4214,7 +4231,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
ent.offsets[0]=add_string(line.gettoken_str(1)); ent.offsets[0]=add_string(line.gettoken_str(1));
if (which_token == TOK_UNREGDLL) if (which_token == TOK_UNREGDLL)
{ {
ent.offsets[1]=add_string(_T("DllUnregisterServer")); ent.offsets[1]=add_asciistring(_T("DllUnregisterServer"));
ent.offsets[2]=DefineInnerLangString(NLF_UNREGISTERING); ent.offsets[2]=DefineInnerLangString(NLF_UNREGISTERING);
} }
else if (which_token == TOK_CALLINSTDLL) else if (which_token == TOK_CALLINSTDLL)
@ -4232,7 +4249,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
else // register else // register
{ {
ent.offsets[1] = add_string(line.gettoken_str(2)); ent.offsets[1] = add_string(line.gettoken_str(2));
if (!ent.offsets[1]) ent.offsets[1]=add_string(_T("DllRegisterServer")); if (!ent.offsets[1]) ent.offsets[1]=add_asciistring(_T("DllRegisterServer"));
ent.offsets[2]=DefineInnerLangString(NLF_REGISTERING); ent.offsets[2]=DefineInnerLangString(NLF_REGISTERING);
} }
@ -4677,8 +4694,8 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
return add_entry(&ent); return add_entry(&ent);
case TOK_HIDEWINDOW: case TOK_HIDEWINDOW:
ent.which=EW_SHOWWINDOW; ent.which=EW_SHOWWINDOW;
ent.offsets[0]=add_string(_T("$HWNDPARENT")); ent.offsets[0]=add_asciistring(_T("$HWNDPARENT"));
ent.offsets[1]=add_string(_T("0")/*SW_HIDE*/); ent.offsets[1]=add_asciistring(_T("0")/*SW_HIDE*/);
ent.offsets[2]=1; ent.offsets[2]=1;
SCRIPT_MSG(_T("HideWindow\n")); SCRIPT_MSG(_T("HideWindow\n"));
return add_entry(&ent); return add_entry(&ent);
@ -4686,8 +4703,8 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
{ {
int ret; int ret;
ent.which=EW_SHOWWINDOW; ent.which=EW_SHOWWINDOW;
ent.offsets[0]=add_string(_T("$HWNDPARENT")); ent.offsets[0]=add_asciistring(_T("$HWNDPARENT"));
ent.offsets[1]=add_string(_T("5")/*SW_SHOW*/); ent.offsets[1]=add_asciistring(_T("5")/*SW_SHOW*/);
ret = add_entry(&ent); ret = add_entry(&ent);
if (ret != PS_OK) return ret; if (ret != PS_OK) return ret;
ent.which=EW_BRINGTOFRONT; ent.which=EW_BRINGTOFRONT;
@ -5343,7 +5360,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
if (line.getnumtokens() == 3) if (line.getnumtokens() == 3)
ent.offsets[1]=add_string(line.gettoken_str(2)); ent.offsets[1]=add_string(line.gettoken_str(2));
else else
ent.offsets[1]=add_string(_T("$TEMP")); ent.offsets[1]=add_asciistring(_T("$TEMP"));
if (ent.offsets[0]<0) PRINTHELP() if (ent.offsets[0]<0) PRINTHELP()
SCRIPT_MSG(_T("GetTempFileName -> %s\n"),line.gettoken_str(1)); SCRIPT_MSG(_T("GetTempFileName -> %s\n"),line.gettoken_str(1));
return add_entry(&ent); return add_entry(&ent);
@ -5415,7 +5432,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
if (ent.offsets[3] != 7 && ent.offsets[3] != 13) ent.offsets[2]=add_string(line.gettoken_str(4)); if (ent.offsets[3] != 7 && ent.offsets[3] != 13) ent.offsets[2]=add_string(line.gettoken_str(4));
if (ent.offsets[3] == 13) { if (ent.offsets[3] == 13) {
ent.offsets[3]=6; ent.offsets[3]=6;
ent.offsets[2]=add_string(_T("0xFFFFFFFF")); ent.offsets[2]=add_asciistring(_T("0xFFFFFFFF"));
} }
SCRIPT_MSG(_T("IntOp: %s=%s%s%s\n"),line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4)); SCRIPT_MSG(_T("IntOp: %s=%s%s%s\n"),line.gettoken_str(1),line.gettoken_str(2),line.gettoken_str(3),line.gettoken_str(4));
return add_entry(&ent); return add_entry(&ent);
@ -5774,7 +5791,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
ent.which=EW_FGETS; ent.which=EW_FGETS;
ent.offsets[0]=GetUserVarIndex(line, 1); // file handle ent.offsets[0]=GetUserVarIndex(line, 1); // file handle
ent.offsets[1]=GetUserVarIndex(line, 2); // output string ent.offsets[1]=GetUserVarIndex(line, 2); // output string
ent.offsets[2]=add_string(_T("1")); ent.offsets[2]=add_asciistring(_T("1"));
ent.offsets[3]=1; ent.offsets[3]=1;
if (ent.offsets[0]<0 || ent.offsets[1]<0) PRINTHELP() if (ent.offsets[0]<0 || ent.offsets[1]<0) PRINTHELP()
SCRIPT_MSG(_T("FileReadByte: %s->%s\n"),line.gettoken_str(1),line.gettoken_str(2)); SCRIPT_MSG(_T("FileReadByte: %s->%s\n"),line.gettoken_str(1),line.gettoken_str(2));
@ -5825,7 +5842,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
ent.which=EW_FGETWS; ent.which=EW_FGETWS;
ent.offsets[0]=GetUserVarIndex(line, 1); // file handle ent.offsets[0]=GetUserVarIndex(line, 1); // file handle
ent.offsets[1]=GetUserVarIndex(line, 2); // output string ent.offsets[1]=GetUserVarIndex(line, 2); // output string
ent.offsets[2]=add_string(_T("1")); ent.offsets[2]=add_asciistring(_T("1"));
ent.offsets[3]=1; ent.offsets[3]=1;
if (ent.offsets[0]<0 || ent.offsets[1]<0) PRINTHELP() if (ent.offsets[0]<0 || ent.offsets[1]<0) PRINTHELP()
SCRIPT_MSG(_T("FileReadWord: %s->%s\n"),line.gettoken_str(1),line.gettoken_str(2)); SCRIPT_MSG(_T("FileReadWord: %s->%s\n"),line.gettoken_str(1),line.gettoken_str(2));
@ -6459,7 +6476,7 @@ int CEXEBuild::do_add_file(const TCHAR *lgss, int attrib, int recurse, int *tota
if (recurse) { if (recurse) {
// save $OUTDIR into $_OUTDIR [StrCpy $_OUTDIR $OUTDIR] // save $OUTDIR into $_OUTDIR [StrCpy $_OUTDIR $OUTDIR]
if (add_entry_direct(EW_ASSIGNVAR, m_UserVarNames.get(_T("_OUTDIR")), add_string(_T("$OUTDIR"))) != PS_OK) { if (add_entry_direct(EW_ASSIGNVAR, m_UserVarNames.get(_T("_OUTDIR")), add_asciistring(_T("$OUTDIR"))) != PS_OK) {
return PS_ERROR; return PS_ERROR;
} }
} }
@ -6540,7 +6557,7 @@ int CEXEBuild::do_add_file(const TCHAR *lgss, int attrib, int recurse, int *tota
SCRIPT_MSG(_T("%sFile: Returning to: \"%s\"\n"), generatecode ? _T("") : _T("Reserve"), dir.c_str()); SCRIPT_MSG(_T("%sFile: Returning to: \"%s\"\n"), generatecode ? _T("") : _T("Reserve"), dir.c_str());
// restore $OUTDIR from $_OUTDIR [SetOutPath $_OUTDIR] // restore $OUTDIR from $_OUTDIR [SetOutPath $_OUTDIR]
if (add_entry_direct(EW_CREATEDIR, add_string(_T("$_OUTDIR")), 1) != PS_OK) { if (add_entry_direct(EW_CREATEDIR, add_asciistring(_T("$_OUTDIR")), 1) != PS_OK) {
return PS_ERROR; return PS_ERROR;
} }
} }
@ -6778,7 +6795,7 @@ int CEXEBuild::do_add_file_create_dir(const tstring& local_dir, const tstring& d
#ifdef _WIN32 #ifdef _WIN32
if (attrib) { if (attrib) {
int ndc = add_string(_T(".")); int ndc = add_asciistring(_T("."));
DWORD attr = GetFileAttributes(local_dir.c_str()); DWORD attr = GetFileAttributes(local_dir.c_str());

View file

@ -17,98 +17,177 @@
*/ */
#include "strlist.h" #include "strlist.h"
#include "utf.h"
MLStringList::MLStringList()
{
m_gr.set_zeroing(1);
#ifdef _UNICODE
m_grAnsi.set_zeroing(1);
#endif
}
#ifdef _UNICODE #ifdef _UNICODE
char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage); // defined in build.cpp 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 #endif
int MLStringList::add(const TCHAR *str, WORD codepage /*= CP_ACP*/, bool processed, bool build_unicode) static inline bool byte_rev_match(const void*ptr1, const void*ptr2, size_t cb)
{ {
#ifndef _UNICODE char *p1 = (char*) ptr1, *p2 = (char*) ptr2;
int a=find(str,2); if (cb) for(; --cb;) if (p1[cb] != p2[cb]) return false;
if (a >= 0) return true;
return a; }
int len = _tcslen(str)+1;
return m_gr.add(str,len*sizeof(TCHAR))/sizeof(TCHAR); unsigned int ExeHeadStringList::getnum() const
#else {
if (build_unicode) char *p = (char*) m_gr.get();
if (!p) return 1; // The empty string always exists
unsigned int cbList = gettotalsize(), cb = 0, num = 1, pos;
pos = 1 + !!m_wide, p += pos; // Skip empty string
if (m_wide)
{ {
int a=find(str,2); for(;;)
if (a >= 0) {
return a; if (pos+=cb >= cbList) break;
cb = StrLenUTF16LE(p+=cb) + 1, ++num;
}
} }
// 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 else
cbMultiByte = WideCharToMultiByte(codepage, 0, str, len, ansiBuf, len*2, NULL, NULL);
if (!build_unicode)
{ {
int a=findAnsi(ansiBuf,2); for(;;)
if (a >= 0)
{ {
delete[] ansiBuf; if (pos+=cb >= cbList) break;
return a; cb = strlen(p+=cb) + 1, ++num;
} }
} }
// string not found, add it return num;
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 + STD_MAX(len,cbMultiByte);
m_gr.resize(len*sizeof(TCHAR));
m_grAnsi.resize(len);
}
return a;
#endif
} }
bool ExeHeadStringList::get(unsigned int offset, tstring&outstr) const
{
if (0 == offset)
{
outstr.clear();
return true;
}
char *p = (char*) m_gr.get();
unsigned int cbList = gettotalsize();
if (p && cbList < offset)
{
if (m_wide)
StrSetUTF16LE(outstr,&p[offset*WIDEDIV]);
else
// BUGBUG: There is no way for us to know the correct codepage
outstr = CtoTString(&p[offset]);
return true;
}
return false;
}
/*
* find() finds the offset where the string is stored, returns -1 if not found.
* It only compares raw byte values, there is no Unicode normalization handling.
* If ppBufMB is non-null you must delete[] it (Only valid when m_wide is false)!
*/
unsigned int ExeHeadStringList::find(const TCHAR *str, WORD codepage, bool processed, char**ppBufMB) const
{
if (m_wide && *str)
{
WCToUTF16LEHlpr cnv;
if (!cnv.Create(str)) return -1;
unsigned int pos = find(cnv.Get(),StrLenUTF16LE(cnv.Get()),codepage,processed,ppBufMB);
cnv.Destroy();
return pos;
}
else
{
return find(str,_tcslen(str),codepage,processed,ppBufMB);
}
}
unsigned int ExeHeadStringList::find(const void *ptr, unsigned int cchF, WORD codepage, bool processed, char**ppBufMB) const
{
const wchar_t *find = (const wchar_t*) ptr; // Data is: m_wide ? UTF16LE : wchar_t
if (!*find) return 0; // The empty string is always first.
char *p = (char*) m_gr.get();
if (!p) return -1;
unsigned int cbF = ++cchF * 2; // Include \0 as part of cchF, * 2 for UTF16 & DBCS.
char *bufMB = 0;
if (!m_wide)
{
unsigned int cbMB;
bufMB = new char[cbF];
if (processed)
{
char *pTmp = convert_processed_string_to_ansi(bufMB,find,codepage);
cbMB = pTmp ? pTmp - bufMB : 0;
}
else
{
cbMB = WideCharToMultiByte(codepage,0,find,cchF,bufMB,cbF,0,0);
}
assert(cbMB);
cbF = cbMB, find = (const wchar_t*) bufMB;
}
unsigned int cbList = gettotalsize(), cb = 0, retval = -1, pos;
pos = 1 + !!m_wide, p += pos; // Skip empty string
if (m_wide)
{
for(;;)
{
if (pos+=cb >= cbList) break;
cb = (StrLenUTF16LE(p+=cb) + 1) * 2;
if (cb < cbF) continue;
if (byte_rev_match(p,find,cbF)) { retval = pos / WIDEDIV; break; }
}
}
else
{
for(;;)
{
if (pos+=cb >= cbList) break;
cb = strlen(p+=cb) + 1;
if (cb < cbF) continue;
if (byte_rev_match(p,find,cbF)) { retval = pos; break; }
}
if (ppBufMB)
*ppBufMB = bufMB;
else
delete[] bufMB;
}
return retval;
}
int ExeHeadStringList::add(const TCHAR *str, WORD codepage, bool processed)
{
char *p = (char*) m_gr.get();
if (!p)
{
if (!*str) return 0; // Delay allocating the empty string
char *&zero = p, cb = 1 + !!m_wide;
unsigned int pos = m_gr.add(&zero,cb);
assert(0 == pos);
}
char *bufMB = 0;
unsigned int pos = find(str,codepage,processed,m_wide ? 0 : &bufMB);
if (-1 != pos)
{
delete[] bufMB;
return pos;
}
if (m_wide)
{
WCToUTF16LEHlpr cnv;
if (!cnv.Create(str)) throw std::bad_alloc();
pos = m_gr.add(cnv.Get(),cnv.GetSize()) / WIDEDIV;
cnv.Destroy();
}
else
{
unsigned int cbMB = strlen(bufMB) + 1;
pos = m_gr.add(bufMB,cbMB);
delete[] bufMB;
}
return pos;
}
int StringList::add(const TCHAR *str, int case_sensitive) int StringList::add(const TCHAR *str, int case_sensitive)
{ {
int a=find(str,case_sensitive); int a=find(str,case_sensitive);
@ -232,7 +311,7 @@ int DefineList::add(const TCHAR *name, const TCHAR *value/*=_T("")*/)
extern void quit(); extern void quit();
if (g_display_errors) if (g_display_errors)
{ {
PrintColorFmtMsg_ERR(_T("\nInternal compiler error #12345: GrowBuf realloc/malloc(%lu) failed.\n"), (unsigned long) size_in_bytes); PrintColorFmtMsg_ERR(_T("\nInternal compiler error #12345: DefineList malloc(%lu) failed.\n"), (unsigned long) size_in_bytes);
} }
quit(); quit();
} }

View file

@ -118,31 +118,42 @@ protected:
GrowBuf m_gr; GrowBuf m_gr;
}; };
#include "tstring.h"
/** /**
* Similar to StringList with case_sensitive=2, but stores strings as both Unicode AND ANSI (codepaged) * Stores a list of strings as UTF16LE or raw bytes (multibyte char*).
*/ */
class MLStringList : private StringList class ExeHeadStringList
{ {
private: // don't copy instances private: // don't copy instances
MLStringList(const MLStringList&); ExeHeadStringList(const ExeHeadStringList&);
void operator=(const MLStringList&); void operator=(const ExeHeadStringList&);
public: public:
MLStringList(); ExeHeadStringList()
~MLStringList() {} {
m_wide = false;
m_gr.set_zeroing(true);
}
int add(const TCHAR *str, WORD codepage, bool processed, bool build_unicode); void setunicode(bool unicode) { m_wide = unicode; }
int getnum() const { return StringList::getnum(); } bool addemptystring() { return true; } // Added by add() when the first real string is added
int getcount() const { return StringList::getcount(); } int add(const TCHAR *str, WORD codepage, bool processed);
const TCHAR *getTchar() const { return (const TCHAR*) m_gr.get(); } unsigned int find(const TCHAR *str, WORD codepage, bool processed, char**ppBufMB) const;
#ifdef _UNICODE bool get(unsigned int offset, tstring&str) const;
const char *getAnsi() const { return (const char*) m_grAnsi.get(); } unsigned int getnum() const;
int findAnsi(const char *str, int case_sensitive) const; unsigned int gettotalsize() const { return m_gr.get() ? m_gr.getlen() : (m_wide ? 2 : 1); }
private: void* getstorageptr() const { return m_gr.get() ? m_gr.get() : L""; }
GrowBuf m_grAnsi;
#endif protected:
unsigned int find(const void *str, unsigned int cchF, WORD codepage, bool processed, char**ppBufMB) const;
GrowBuf m_gr;
bool m_wide;
enum {WIDEDIV=2}; // ExeHead expects us to provide offsets this way, also helps UTF16 offsets for shell constants to fit in < 0xFF
}; };
/** /**
* This class maintains a list of T types in a GrowBuf sorted by T.name which * 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 * is assumed to be a string (TCHAR*). So it's really sort of a
@ -182,14 +193,15 @@ class SortedStringList
T newstruct={0,}; T newstruct={0,};
int pos=find(name,case_sensitive,1); int pos=find(name,case_sensitive,1);
if (pos==-1) return -1; if (pos==-1) return -1;
newstruct.name=(TCHAR*)malloc((_tcslen(name)+1)*sizeof(TCHAR)); const UINT cbName=(_tcslen(name)+1)*sizeof(TCHAR);
newstruct.name=(TCHAR*)malloc(cbName);
if (!newstruct.name) if (!newstruct.name)
{ {
extern int g_display_errors; extern int g_display_errors;
extern void quit(); extern void quit();
if (g_display_errors) if (g_display_errors)
{ {
PrintColorFmtMsg_ERR(_T("\nInternal compiler error #12345: GrowBuf realloc/malloc(%lu) failed.\n"),(unsigned long)((_tcslen(name)+1)*sizeof(TCHAR))); PrintColorFmtMsg_ERR(_T("\nInternal compiler error #12345: SortedStringList malloc(%lu) failed.\n"),(unsigned long)cbName);
} }
quit(); quit();
} }

View file

@ -247,7 +247,7 @@ static tokenType tokenlist[TOK__LAST] =
{TOK_P_SYSTEMEXEC,_T("!system"),1,2,_T("command [<|>|<>|=) retval]"),TP_ALL}, {TOK_P_SYSTEMEXEC,_T("!system"),1,2,_T("command [<|>|<>|=) retval]"),TP_ALL},
{TOK_P_EXECUTE,_T("!execute"),1,0,_T("command"),TP_ALL}, {TOK_P_EXECUTE,_T("!execute"),1,0,_T("command"),TP_ALL},
{TOK_P_ADDINCLUDEDIR,_T("!AddIncludeDir"),1,0,_T("dir"),TP_ALL}, {TOK_P_ADDINCLUDEDIR,_T("!AddIncludeDir"),1,0,_T("dir"),TP_ALL},
{TOK_P_INCLUDE,_T("!include"),1,1,_T("[/NONFATAL] filename.nsh"),TP_ALL}, {TOK_P_INCLUDE,_T("!include"),1,2,_T("[/NONFATAL] [/CHARSET=<") TSTR_INPUTCHARSET _T(">] filename.nsh"),TP_ALL},
{TOK_P_CD,_T("!cd"),1,0,_T("absolute_or_relative_new_directory"),TP_ALL}, {TOK_P_CD,_T("!cd"),1,0,_T("absolute_or_relative_new_directory"),TP_ALL},
{TOK_P_IF,_T("!if"),1,3,_T("[!] (value [(==,!=,S==,S!=,=,<>,<=,<,>,>=,&,&&,||) value2] | /FILEEXISTS path)"),TP_ALL}, {TOK_P_IF,_T("!if"),1,3,_T("[!] (value [(==,!=,S==,S!=,=,<>,<=,<,>,>=,&,&&,||) value2] | /FILEEXISTS path)"),TP_ALL},
{TOK_P_IFDEF,_T("!ifdef"),1,-1,_T("symbol [| symbol2 [& symbol3 [...]]]"),TP_ALL}, {TOK_P_IFDEF,_T("!ifdef"),1,-1,_T("symbol [| symbol2 [& symbol3 [...]]]"),TP_ALL},
@ -308,7 +308,7 @@ const TCHAR* CEXEBuild::get_commandtoken_name(int tok)
return 0; return 0;
} }
void CEXEBuild::print_help(TCHAR *commandname) void CEXEBuild::print_help(const TCHAR *commandname)
{ {
int x; int x;
for (x = 0; x < TOK__LAST; x ++) for (x = 0; x < TOK__LAST; x ++)

View file

@ -17,24 +17,7 @@
#include "utf.h" #include "utf.h"
// BUGBUG: We might want to use MB_ERR_INVALID_CHARS but it is not supported #define FIX_ENDIAN_INT16LETOHOST_INPLACE FIX_ENDIAN_INT16_INPLACE
// on < WinXP or in our current POSIX implementation.
static const int UTF8MBTWCFLAGS = 0;
#define ExeHeadWStrFree free
static EXEHEADWCHAR_T* ExeHeadWStrAlloc(UINT cch)
{
EXEHEADWCHAR_T* s = (EXEHEADWCHAR_T*) malloc(cch*sizeof(EXEHEADWCHAR_T));
#if 0
// TODO: We should add POSIX versions of G/SetLastError
// if we want to tell _why_ UTF8ToExeHeadTStr failed...
if (!s) SetLastError(ERROR_OUTOFMEMORY);
#endif
return s;
}
#ifdef _UNICODE
void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch) void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch)
{ {
@ -43,51 +26,326 @@ void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch)
if (!empty) *out = 0; if (!empty) *out = 0;
} }
#else // !_UNICODE UINT StrLenUTF16LE(const void*str)
EXEHEADTCHAR_T* UTF8ToExeHeadTStrDup(LPCSTR StrU8,UINT Codepage)
{ {
int cchW = MultiByteToWideChar(CP_UTF8,UTF8MBTWCFLAGS,StrU8,-1,NULL,0); unsigned short *p = (unsigned short *) str;
if (!cchW) return NULL; for(;*p;) ++p;
WCHAR *bufWStr = (WCHAR*) ExeHeadWStrAlloc(cchW); UINT cch = 0;
if (!bufWStr) return NULL; if ((size_t)p > (size_t)str) cch = ((size_t)p - (size_t)str) - 1;
EXEHEADTCHAR_T *outstr = NULL; return cch;
if (MultiByteToWideChar(CP_UTF8,UTF8MBTWCFLAGS,StrU8,-1,bufWStr,cchW)) }
bool StrSetUTF16LE(tstring&dest, const void*src)
{
#ifdef _WIN32
dest = (unsigned short *) src;
#else
#error TODO: UTF16LE to wchar_t
#endif
return true;
}
inline UINT UTF8ToWC_Convert(LPCSTR StrU8,UINT cbU8,wchar_t*Buffer,UINT cchBuf)
{
#ifndef MB_ERR_INVALID_CHARS
const UINT MB_ERR_INVALID_CHARS = 8; // MSDN says this flag is OK for CP_UTF8
#endif
return (UINT) MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,StrU8,cbU8,Buffer,cchBuf);
}
inline UINT UTF8ToWC_Prepare(LPCSTR StrU8,UINT cbU8)
{
return UTF8ToWC_Convert(StrU8,cbU8,0,0);
}
wchar_t* DupWCFromBytes(void*Buffer,UINT cbBuffer,WORD SrcCP)
{
/*\
Converts a buffer encoded with SrcCP to a \0 terminated wchar_t malloc'ed buffer.
Returns 0 if malloc failed or -1 if conversion to wchar_t failed.
\*/
NStreamEncoding srcenc(SrcCP);
wchar_t*pwc = 0;
#ifdef _WIN32
if (srcenc.IsUTF16LE())
{ {
int cbA = WideCharToMultiByte(Codepage,0,bufWStr,cchW,NULL,0,NULL,NULL); // Assuming wchar_t==UTF16LE
if (cbA && (outstr = ExeHeadTStrAlloc(cbA))) pwc = (wchar_t*) malloc(cbBuffer + 2);
if (!pwc) return pwc;
memcpy(pwc, Buffer, cbBuffer);
*((wchar_t*)(((char*)pwc)+cbBuffer)) = L'\0';
return pwc;
}
// TODO: MBTWC on Windows is lame, we are going to fail if SrcCP is UTF16BE or UTF32
#endif
UINT cchW = MultiByteToWideChar(SrcCP,0,(char*)Buffer,cbBuffer,0,0);
if (!cchW && NStreamEncoding::GetCodeUnitSize(SrcCP) <= cbBuffer)
{
return (wchar_t*)-1;
}
pwc = (wchar_t*) malloc((cchW+1)*sizeof(wchar_t));
if (!pwc) return pwc;
MultiByteToWideChar(SrcCP,0,(char*)Buffer,cbBuffer,pwc,cchW);
pwc[cchW] = L'\0';
return pwc;
}
UINT DetectUTFBOM(FILE*strm)
{
/*\
Tries to detect a BOM at the start of a stream. If a BOM is found it is eaten.
NOTE: ungetc is only guaranteed to support 1 pushback,
lets hope no MBCS file starts with parts of a BOM.
\*/
const int b1 = fgetc(strm);
if (EOF == b1) return 0;
if (0xef == b1)
{
const int b2 = fgetc(strm);
if (0xbb == b2)
{ {
if (!WideCharToMultiByte(Codepage,0,bufWStr,cchW,outstr,cbA,NULL,NULL)) const int b3 = fgetc(strm);
if (0xbf == b3) return NStreamEncoding::UTF8;
ungetc(b3,strm);
}
ungetc(b2,strm);
}
if (0xfe == b1 || 0xff == b1 || 0x00 == b1)
{
const int b2 = fgetc(strm), b3 = fgetc(strm);
if (b1 && (b1^b2) == (0xfe^0xff))
{
if (0xff == b1 && 0 == b3)
{ {
free(outstr); const int b4 = fgetc(strm);
outstr = NULL; if (0 == b4) return NStreamEncoding::UTF32LE;
ungetc(b4,strm);
}
ungetc(b3,strm);
return 0xff == b1 ? NStreamEncoding::UTF16LE : NStreamEncoding::UTF16BE;
}
if (0 == b1 && 0 == b2)
{
if (0xfe == b3)
{
const int b4 = fgetc(strm);
if (0xff == b4) return NStreamEncoding::UTF32BE;
ungetc(b4,strm);
} }
} }
ungetc(b3,strm);
ungetc(b2,strm);
} }
ExeHeadWStrFree(bufWStr); ungetc(b1,strm);
return outstr; return 0;
} }
#endif // ?_UNICODE WORD GetEncodingFromString(const TCHAR*s)
bool IsUTF8BOM(FILE*fstrm)
{ {
// ungetc is only guaranteed to support 1 pushback, if (!_tcsicmp(s,_T("ACP"))) return NStreamEncoding::ACP;
// lets hope no ASCII file starts with 0xEF and is not a BOM! if (!_tcsicmp(s,_T("OEM"))) return NStreamEncoding::OEMCP;
const int c = fgetc(fstrm); if (!_tcsicmp(s,_T("UTF8"))) return NStreamEncoding::UTF8;
if (EOF == c) return false; if (!_tcsicmp(s,_T("UTF16LE"))) return NStreamEncoding::UTF16LE;
if (0xef == c) if (!_tcsicmp(s,_T("UTF16BE"))) return NStreamEncoding::UTF16BE;
if (S7IsChEqualI('C',*s++) && S7IsChEqualI('P',*s++))
{ {
const int c2 = fgetc(fstrm); int cp = _tstoi(s);
if (0xbb == c2) if (cp > 0 && cp < NStreamEncoding::CPCOUNT) return (WORD) cp;
{
const int c3 = fgetc(fstrm);
if (0xbf == c3) return true;
ungetc(c3,fstrm);
}
ungetc(c2,fstrm);
} }
ungetc(c,fstrm); return NStreamEncoding::UNKNOWN;
return false; }
void NStreamEncoding::GetCPDisplayName(WORD CP, TCHAR*Buf)
{
TCHAR mybuf[10], *p = mybuf;
switch(CP)
{
case ACP: p = _T("ACP"); break;
case OEMCP: p = _T("OEM"); break;
case UTF16LE: p = _T("UTF16LE"); break;
case UTF16BE: p = _T("UTF16BE"); break;
case UTF32LE: p = _T("UTF32LE"); break;
case UTF32BE: p = _T("UTF32BE"); break;
case UTF8: p = _T("UTF8"); break;
default:
_stprintf(mybuf,_T("CP%u"),CP);
if (CP >= NStreamEncoding::CPCOUNT) p = _T("?");
}
_tcscpy(Buf,p);
}
tstring NStreamLineReader::GetErrorMessage(UINT Error, const TCHAR*Filename, UINT Line)
{
tstring msg;
TCHAR buf[40];
switch(Error)
{
case NStream::ERR_BUFFEROVERFLOW:
msg = _T("Line too long: ");
break;
case NStream::ERR_IOERROR:
msg = _T("I/O error"), Filename = 0;
break;
case NStream::ERR_UNSUPPORTEDENCODING:
StreamEncoding().GetCPDisplayName(buf);
msg = tstring(buf) + _T(" is not supported"), Filename = 0;
break;
default:
msg = _T("Bad text encoding: ");
break;
}
if (Filename)
{
_stprintf(buf,_T("%u"),Line);
msg = msg + Filename + _T(":") + buf;
}
return msg + _T("\n");
}
UINT NStreamLineReader::ReadLine(wchar_t*Buffer, UINT cchBuf)
{
/*\
Reads from the associated stream until it finds a new-line or
the read fails (I/O error or EOF). It fails with ERR_BUFFEROVERFLOW if
cchBuf-1 wchar_t's are read without finding the end of the line.
Buffer MUST be a valid pointer, it will be \0 terminated as long as cchBuf > 0.
\*/
if (!cchBuf) return NStream::ERR_BUFFEROVERFLOW;
#ifndef MB_ERR_INVALID_CHARS
const UINT MB_ERR_INVALID_CHARS = 8;
#endif
const UINT cchFullBuf = cchBuf;
NIStream&strm = GetStream();
l_restart:
// Only supports MBCS and UTF-8 for now...
if (StreamEncoding().IsUTF8())
{
for(;;)
{
BYTE cb = 0; // bytes in chU8 -1
BYTE chU8[6];
if (!strm.ReadOctet(&chU8[0])) goto l_ioerror;
UINT cchWC;
#if defined(WIN32) // TODO: Is wchar_t==UTF16LE under cygwin?
// Fast path if wchar_t == UTF16 and in ASCII range
if (chU8[0] <= 127 && sizeof(wchar_t) == 2)
{
cchWC = ++cb;
if (cchBuf <= cchWC) goto l_lineoverflow;
*Buffer = (wchar_t) chU8[0];
}
else
#endif
{
if (0xC0 == (0xC0 & chU8[0]))
{
++cb;
if (0xE0 == (0xE0 & chU8[0]))
{
++cb;
if (0xF0 == (0xF0 & chU8[0]))
{
++cb;
if (0xF8 == (0xF8 & chU8[0]))
{
++cb;
if (0xFC == (0xFE & chU8[0]))
++cb;
else
goto l_badutf;
}
}
}
}
for(BYTE moreU8 = 0; moreU8 < cb;)
{
BYTE b;
if (!strm.ReadOctet(&b)) goto l_ioerror;
if (0x80 != (0xC0 & b)) goto l_badutf; // chU8[1..n] must be 0b10xxxxxx
chU8[++moreU8] = b;
}
++cb;
cchWC = UTF8ToWC_Prepare((LPCSTR)chU8,cb);
if (!cchWC) goto l_badutf;
if (cchBuf <= cchWC) goto l_lineoverflow;
cchWC = UTF8ToWC_Convert((LPCSTR)chU8,cb,Buffer,cchWC);
}
if (CompleteLine(Buffer,cchWC,cchBuf,true)) goto l_success;
}
}
#ifdef _WIN32
else if (StreamEncoding().IsUTF16LE())
{
unsigned short lead, trail, cchWC;
for(;;)
{
if (!strm.ReadInt16(&lead)) goto l_ioerror;
FIX_ENDIAN_INT16LETOHOST_INPLACE(lead);
if (IsTrailSurrogateUTF16(lead)) goto l_badutf;
UINT32 codpt = lead;
Buffer[0] = lead, cchWC = 0;
if (IsLeadSurrogateUTF16(lead))
{
if (!strm.ReadInt16(&trail)) goto l_ioerror;
FIX_ENDIAN_INT16LETOHOST_INPLACE(trail);
if (!IsTrailSurrogateUTF16(trail)) goto l_badutf;
codpt = CodePointFromUTF16SurrogatePair(lead,trail);
Buffer[1] = trail, ++cchWC;
}
if (!IsValidUnicodeCodePoint(codpt)) goto l_badutf;
if (CompleteLine(Buffer,++cchWC,cchBuf,true)) goto l_success;
}
}
#endif
else if (StreamEncoding().IsUnicode())
{
goto l_unsupportedencoding;
}
else
{
const UINT cp = StreamEncoding().GetCodepage();
UINT mbtowcflags = 0;
if (cp < 50220 && cp != 42) mbtowcflags = MB_ERR_INVALID_CHARS;
for(;;)
{
BYTE bufMB[2];
BYTE mb = 0;
if (!strm.ReadOctet(&bufMB[0])) goto l_ioerror;
if (IsDBCSLeadByteEx(cp,bufMB[0]))
{
if (!strm.ReadOctet(&bufMB[++mb])) goto l_ioerror;
}
++mb;
UINT cchWC = MultiByteToWideChar(cp,mbtowcflags,(LPCSTR)bufMB,mb,0,0);
if (!cchWC) goto l_badencoding;
if (cchBuf <= cchWC) goto l_lineoverflow;
cchWC = MultiByteToWideChar(cp,mbtowcflags,(LPCSTR)bufMB,mb,Buffer,cchWC);
if (CompleteLine(Buffer,cchWC,cchBuf,false)) goto l_success;
}
}
l_ioerror:
*Buffer = 0;
return NStream::ERR_IOERROR;
l_lineoverflow:
*Buffer = 0;
return NStream::ERR_BUFFEROVERFLOW;
l_badutf:
l_badencoding:
*Buffer = 0;
return NStream::ERR_INVALIDENCODING;
l_unsupportedencoding:
*Buffer = 0;
return NStream::ERR_UNSUPPORTEDENCODING;
l_success:
*Buffer = 0;
// "Foo\r\nBar" is 2 and not 3 lines
const wchar_t chThisNL = *--Buffer, chPrevNL = m_PrevNL;
const bool onlyNL = ++cchBuf == cchFullBuf;
m_PrevNL = chThisNL;
if (onlyNL && (chPrevNL^chThisNL) == ('\r'^'\n'))
{
m_PrevNL = 0;
goto l_restart; // Previous line was "Foo\r". This line was "\n", ignore it.
}
return NStream::OK;
} }

View file

@ -21,41 +21,244 @@
#include "Platform.h" #include "Platform.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include "util.h" // For my_fopen
typedef unsigned short EXEHEADWCHAR_T; #define TSTR_INPUTCHARSET _T("ACP|OEM|CP#|UTF8")
#ifdef _UNICODE
typedef EXEHEADWCHAR_T EXEHEADTCHAR_T;
void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch); void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch);
#else // !_UNICODE
typedef char EXEHEADTCHAR_T;
inline EXEHEADTCHAR_T* ExeHeadTStrAlloc(UINT cb)
{
// NOTE: UTF8ToExeHeadTStrDup calls this so we are required to use malloc
return (EXEHEADTCHAR_T*) malloc(cb);
}
extern EXEHEADTCHAR_T* UTF8ToExeHeadTStrDup(LPCSTR StrU8,UINT Codepage);
inline void RawTStrToASCII(const TCHAR*in,char*out,UINT maxcch) { lstrcpyn(out,in,maxcch); }
#endif // ?_UNICODE
template<typename T> T S7ChLwr(T c) { return c>='A' && c<='Z' ? (T)(c|32) : c; } template<typename T> T S7ChLwr(T c) { return c>='A' && c<='Z' ? (T)(c|32) : c; }
template<typename T> T S7ChUpr(T c) { return c>='a' && c<='z' ? (T)(c-'a'+'A') : c; } template<typename T> T S7ChUpr(T c) { return c>='a' && c<='z' ? (T)(c-'a'+'A') : c; }
template<typename T> bool S7IsChEqualI(char ch,T cmp) template<typename T> bool S7IsChEqualI(char ch,T cmp)
{ {
return cmp==(T)S7ChLwr(ch) || cmp==(T)S7ChUpr(ch); return S7ChLwr((T)ch) == S7ChLwr(cmp);
} }
inline bool IsValidUnicodeCodePoint(UINT32 c,bool StrictUTF32=false)
{
// Unicode 6.1: 16.7 Noncharacters
if ((c&0xfffe) == 0xfffe) return false; // ..FFFE & ..FFFF is reserved in each plane
if (c >= 0xfdd0 && c <= 0xfdef) return false; // Reserved in BMP
if (StrictUTF32 && c > 0x10ffff) return false;
return true;
}
inline bool IsLeadSurrogateUTF16(unsigned short c) { return c >= 0xd800 && c <= 0xdbff; }
inline bool IsTrailSurrogateUTF16(unsigned short c) { return c >= 0xdc00 && c <= 0xdfff; }
inline UINT32 CodePointFromUTF16SurrogatePair(unsigned short lea,unsigned short tra)
{
const UINT32 surrogate_offset = 0x10000 - (0xD800 << 10) - 0xDC00;
return ((UINT32)lea << 10) + tra + surrogate_offset;
}
/** UINT StrLenUTF16LE(const void*str);
* Tries to peek at the first few bytes in the stream to determine if it is a UTF-8 BOM. bool StrSetUTF16LE(tstring&dest, const void*src);
* If it is a UTF-8 BOM it will eat the BOM,
* if it is not it tries its best to restore the data. wchar_t* DupWCFromBytes(void*Buffer,UINT cbBuffer,WORD SrcCP);
*/ UINT DetectUTFBOM(FILE*strm);
extern bool IsUTF8BOM(FILE*fstrm); WORD GetEncodingFromString(const TCHAR*s);
class WCToUTF16LEHlpr {
unsigned short* m_s;
public:
WCToUTF16LEHlpr() : m_s(0) {}
bool Create(const TCHAR*in)
{
#if defined(_WIN32) && defined(_UNICODE)
m_s = (unsigned short*) in;
#else
#error TODO: wchar_t to UTF16LE
#endif
return true;
}
void Destroy()
{
#if !defined(_WIN32) && !defined(_UNICODE)
delete[] m_s;
#endif
}
const unsigned short* Get() const { return m_s; }
UINT GetLen() const { return StrLenUTF16LE(m_s); }
UINT GetSize() const { return (GetLen()+1) * 2; }
};
class NStreamEncoding {
protected:
WORD m_cp;
public:
enum {
ACP = CP_ACP,
OEMCP = 1,
UTF16LE = 1200,
UTF16BE = 1201,
UTF32LE = 12000,
UTF32BE = 12001,
UTF8 = CP_UTF8,
UNKNOWN = (0xffff-0),
AUTO = (0xffff-1),
CPCOUNT = (0xffff-2) // Must be less than our other magic numbers
};
NStreamEncoding() { Reset(); }
NStreamEncoding(WORD cp) { Reset();SetCodepage(cp); }
WORD GetCodepage() const { return m_cp; }
void SetCodepage(WORD cp) { m_cp = cp; }
void SafeSetCodepage(WORD cp)
{
if (NStreamEncoding::AUTO==cp) cp = GetPlatformDefaultCodepage();
if (NStreamEncoding::UNKNOWN==cp) cp = GetPlatformDefaultCodepage();
SetCodepage(cp);
}
void Reset() { SetCodepage(GetPlatformDefaultCodepage()); }
WORD GetPlatformDefaultCodepage() const
{
#ifdef _WIN32
return ACP;
#else
return UTF8;
#endif
}
bool IsUTF8() const { return UTF8==GetCodepage(); }
bool IsUTF16LE() const { return UTF16LE==GetCodepage(); }
bool IsUnicode() const { return IsUnicodeCodepage(GetCodepage()); }
void GetCPDisplayName(TCHAR*Buf) { GetCPDisplayName(m_cp, Buf); }
static UINT GetCodeUnitSize(WORD cp)
{
if ((UTF16LE|1)==(cp|1)) return 2;
if ((UTF32LE|1)==(cp|1)) return 4;
return 1;
}
static bool IsUnicodeCodepage(WORD cp)
{
return UTF8==cp || (UTF16LE|1)==(cp|1) || (UTF32LE|1)==(cp|1);
}
static void GetCPDisplayName(WORD CP, TCHAR*Buf);
};
class NStream {
public:
enum {
OK = 0,
ERR_BUFFEROVERFLOW,
ERR_IOERROR,
ERR_INVALIDENCODING,
ERR_UNSUPPORTEDENCODING,
};
static bool IsNewline(wchar_t chW, bool HandleUnicodeNL)
{
if (L'\n'==chW || L'\r'==chW) return true;
if (HandleUnicodeNL)
{
// www.unicode.org/standard/reports/tr13/tr13-5.html#UNICODE NEWLINE GUIDELINES
if (L'\f'==chW) return true; // FF/Form Feed
if (L'\v'==chW) return true; // VT/Vertical Tab
// NOTIMPLEMENTED: NEL/Next Line/U+0085
// NOTIMPLEMENTED: LS/Line Separator/U+2028
// NOTIMPLEMENTED: PS/Paragraph Separator/U+2029
}
return false;
}
};
class NIStream {
protected:
FILE* m_hFile;
NStreamEncoding m_Enc;
public:
NIStream() : m_hFile(0) {}
~NIStream() { Close(); }
FILE* GetHandle() const { return m_hFile; }
NStreamEncoding& StreamEncoding() { return m_Enc; }
bool IsEOF() const { return feof(m_hFile) != 0; }
bool IsError() const { return ferror(m_hFile) != 0; }
bool IsUnicode() const { return m_Enc.IsUnicode(); }
void Close()
{
FILE*hF = Detach();
if (hF) fclose(hF);
}
bool OpenFileForReading(const TCHAR* Path, WORD enc = NStreamEncoding::AUTO)
{
FILE *hFile = my_fopen(Path, "rb");
return Attach(hFile, enc);
}
bool OpenFileForReading(const TCHAR* Path, NStreamEncoding&Enc)
{
return OpenFileForReading(Path, Enc.GetCodepage());
}
bool OpenStdIn(WORD enc = NStreamEncoding::AUTO)
{
return Attach(stdin, enc);
}
bool OpenStdIn(NStreamEncoding&Enc)
{
return OpenStdIn(Enc.GetCodepage());
}
FILE* Detach()
{
FILE *hFile = m_hFile;
m_hFile = 0;
return hFile;
}
bool Attach(FILE*hFile, WORD enc)
{
Close();
m_hFile = hFile;
if (m_hFile)
{
WORD cp = DetectUTFBOM(m_hFile);
if (!cp) cp = enc;
m_Enc.SafeSetCodepage(cp);
}
return 0 != m_hFile;
}
UINT ReadOctets(void*Buffer, UINT cbBuf)
{
size_t cb = fread(Buffer, 1, cbBuf, m_hFile);
return (UINT) cb;
}
bool ReadOctets(void*Buffer, UINT*pcbBuf)
{
UINT cbReq = *pcbBuf, cb = ReadOctets(Buffer, cbReq);
*pcbBuf = cb;
return cbReq == cb;
}
bool ReadOctet(void*Buffer) { return 1 == ReadOctets(Buffer, 1); }
bool ReadInt16(void*Buffer) { return 2 == ReadOctets(Buffer, 2); }
};
class NStreamLineReader {
protected:
NIStream &m_Strm;
wchar_t m_PrevNL;
public:
NStreamLineReader(NIStream &Strm) : m_Strm(Strm), m_PrevNL(0) {}
NIStream& GetStream() { return m_Strm; }
NStreamEncoding& StreamEncoding() { return m_Strm.StreamEncoding(); }
bool IsEOF() const { return m_Strm.IsEOF(); }
bool IsUnicode() const { return m_Strm.IsUnicode(); }
UINT ReadLine(wchar_t*Buffer, UINT cchBuf);
tstring GetErrorMessage(UINT Error, const TCHAR*Filename=0, UINT Line=0);
protected:
bool CompleteLine(wchar_t*&BufWC, UINT cchWC, UINT&cchRemain, bool HandleUnicodeNL)
{
const wchar_t chW = *BufWC;
BufWC += cchWC, cchRemain -= cchWC;
if (0 == --cchWC) // We only care about code points that fit in a single wchar_t
return NStream::IsNewline(chW, HandleUnicodeNL);
return false;
}
};
#endif // NSIS_UTF_H #endif // NSIS_UTF_H

View file

@ -67,11 +67,28 @@ void dopause(void)
double my_wtof(const wchar_t *str) double my_wtof(const wchar_t *str)
{ {
char buf[100]; char buf[100];
WideCharToMultiByte(0,0,str,-1,buf,100,0,0); WideCharToMultiByte(0,0,str,-1,buf,100,0,0);
return atof(buf); return atof(buf);
} }
unsigned int my_strncpy(TCHAR*Dest, const TCHAR*Src, unsigned int cchMax)
{
// Dest and Src must be valid, Dest is always \0 terminated.
// Returns number of TCHARs copied to Dest; min(strlen(Src),cchMax-1).
unsigned int cch = 0;
if (cchMax)
{
for(;--cchMax;)
{
TCHAR ch = Src[cch];
if (!ch) break;
Dest[cch++] = ch;
}
Dest[cch] = _T('\0');
}
return cch;
}
// Returns 0 if everything is OK // Returns 0 if everything is OK
// Returns -1 if can't find the file // Returns -1 if can't find the file
@ -208,6 +225,7 @@ void static create_code_page_string(TCHAR *buf, size_t len, UINT code_page) {
switch(code_page) switch(code_page)
{ {
case CP_ACP: case CP_ACP:
case 1: // OEMCP
code_page = 1252; code_page = 1252;
break; break;
case CP_UTF8: case CP_UTF8:
@ -226,7 +244,7 @@ int WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWSTR lpWideCharStr,
char cp[128]; char cp[128];
create_code_page_string(cp, sizeof(cp), CodePage); create_code_page_string(cp, sizeof(cp), CodePage);
iconv_t cd = iconv_open(cp, "UCS-2LE"); //TODO: Should "UCS-2LE" be "wchar_t"? iconv_t cd = iconv_open(cp, "wchar_t");
if (cd == (iconv_t) -1) { if (cd == (iconv_t) -1) {
return 0; return 0;
} }
@ -262,7 +280,7 @@ int MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCSTR lpMultiByteStr,
char cp[128]; char cp[128];
create_code_page_string(cp, sizeof(cp), CodePage); create_code_page_string(cp, sizeof(cp), CodePage);
iconv_t cd = iconv_open("UCS-2LE", cp); //TODO: Should "UCS-2LE" be "wchar_t"? iconv_t cd = iconv_open("wchar_t", cp);
if (cd == (iconv_t) -1) { if (cd == (iconv_t) -1) {
return 0; return 0;
} }
@ -296,7 +314,7 @@ BOOL IsValidCodePage(UINT CodePage)
TCHAR cp[128]; TCHAR cp[128];
create_code_page_string(cp, sizeof(cp), CodePage); create_code_page_string(cp, sizeof(cp), CodePage);
iconv_t cd = iconv_open(_T("UCS-2LE"), cp); //TODO: Should "UCS-2LE" be "wchar_t"? iconv_t cd = iconv_open(_T("wchar_t"), cp);
if (cd == (iconv_t) -1) if (cd == (iconv_t) -1)
return FALSE; return FALSE;

View file

@ -36,6 +36,7 @@ extern int g_dopause;
extern void dopause(void); extern void dopause(void);
extern double my_wtof(const wchar_t *str); extern double my_wtof(const wchar_t *str);
extern unsigned int my_strncpy(TCHAR*Dest, const TCHAR*Src, unsigned int cchMax);
// Adds the bitmap in filename using resource editor re as id id. // Adds the bitmap in filename using resource editor re as id id.
// If width or height are specified it will also make sure the bitmap is in that size // If width or height are specified it will also make sure the bitmap is in that size