
The official plugins are now stored in architecture specific subdirectories under NSIS\Plugins. !AddPluginDir also gained a new (optional) architecture flag because MakeNSIS now stores separate plugin information for each target architecture. Storing plugins in the root of the Plugins directory is no longer supported. MinGW does not implement the unicode CRT startup functions so the entry point functions and linker parameters had to be changed. The unicode tools use the ansi entry point and a small helper function that calls into the real code: _tmain has full argc+argv emulation while wWinMain does not pass the command line parameters. The stubs do not use any CRT functions and have no CRT or unicode helper code, they call our entry point directly. git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6269 212acab6-be3b-0410-9dea-997c60f758d6
3783 lines
108 KiB
C++
3783 lines
108 KiB
C++
/*
|
|
* build.cpp
|
|
*
|
|
* This file is a part of NSIS.
|
|
*
|
|
* Copyright (C) 1999-2009 Nullsoft and Contributors
|
|
*
|
|
* Licensed under the zlib/libpng license (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
*
|
|
* Licence details can be found in the file COPYING.
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty.
|
|
*
|
|
* Unicode support added by Jim Park -- 08/07/2007
|
|
*/
|
|
|
|
#include "tchar.h"
|
|
#include "Platform.h"
|
|
#include <stdio.h>
|
|
#include "exehead/config.h"
|
|
|
|
#include <nsis-version.h>
|
|
|
|
#include "build.h"
|
|
#include "util.h"
|
|
#include "fileform.h"
|
|
#include "writer.h"
|
|
#include "crc32.h"
|
|
#include "manifest.h"
|
|
#include "icon.h"
|
|
|
|
#include "exehead/api.h"
|
|
#include "exehead/resource.h"
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "ResourceEditor.h"
|
|
#include "DialogTemplate.h"
|
|
#include "ResourceVersionInfo.h"
|
|
#include "tstring.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#ifndef _WIN32
|
|
# include <locale.h>
|
|
# include <unistd.h>
|
|
# include <limits.h>
|
|
# include <stdlib.h>
|
|
#endif
|
|
|
|
#include <cassert> // for assert
|
|
|
|
#define RET_UNLESS_OK( function_rc ) do { \
|
|
int rc = (function_rc); \
|
|
if ( rc != PS_OK) \
|
|
return rc; \
|
|
} while (false)
|
|
|
|
using namespace std;
|
|
|
|
namespace { // begin anonymous namespace
|
|
|
|
bool isSimpleChar(TCHAR ch)
|
|
{
|
|
return (ch == _T('.') ) || (ch == _T('_') ) || (ch >= _T('0') && ch <= _T('9')) || (ch >= _T('A') && ch <= _T('Z')) || (ch >= _T('a') && ch <= _T('z'));
|
|
}
|
|
|
|
} // end of anonymous namespace
|
|
|
|
void CEXEBuild::define(const TCHAR *p, const TCHAR *v)
|
|
{
|
|
definedlist.add(p,v);
|
|
}
|
|
|
|
CEXEBuild::~CEXEBuild()
|
|
{
|
|
free_loaded_icon(installer_icon);
|
|
free_loaded_icon(uninstaller_icon);
|
|
|
|
delete [] m_exehead;
|
|
|
|
int nlt = lang_tables.getlen() / sizeof(LanguageTable);
|
|
LanguageTable *nla = (LanguageTable*)lang_tables.get();
|
|
|
|
for (int i = 0; i < nlt; i++) {
|
|
DeleteLangTable(nla+i);
|
|
}
|
|
|
|
for (;postbuild_cmds;)
|
|
{
|
|
struct postbuild_cmd * tmp = postbuild_cmds;
|
|
postbuild_cmds = postbuild_cmds->next;
|
|
delete [] tmp;
|
|
}
|
|
}
|
|
|
|
CEXEBuild::CEXEBuild() :
|
|
m_exehead(0),
|
|
m_exehead_size(0)
|
|
{
|
|
linecnt = 0;
|
|
fp = 0;
|
|
curfilename = 0;
|
|
curfile_unicode = FALSE;
|
|
|
|
display_info=1;
|
|
display_script=1;
|
|
display_errors=1;
|
|
display_warnings=1;
|
|
|
|
cur_ifblock=NULL;
|
|
last_line_had_slash=0;
|
|
inside_comment=false;
|
|
multiple_entries_instruction=0;
|
|
|
|
build_include_depth=0;
|
|
#ifndef _UNICODE
|
|
build_include_isutf8=false;
|
|
#endif
|
|
|
|
has_called_write_output=false;
|
|
|
|
ns_func.add(_T(""),0); // make sure offset 0 is special on these (i.e. never used by a label)
|
|
ns_label.add(_T(""),0);
|
|
|
|
definedlist.add(_T("NSIS_VERSION"), NSIS_VERSION);
|
|
#ifdef NSIS_PACKEDVERSION
|
|
definedlist.add(_T("NSIS_PACKEDVERSION"), NSIS_PACKEDVERSION);
|
|
#endif
|
|
|
|
build_unicode=false;
|
|
m_target_type=TARGET_X86ANSI;
|
|
|
|
// automatically generated header file containing all defines
|
|
#include <nsis-defines.h>
|
|
|
|
// no longer optional
|
|
definedlist.add(_T("NSIS_SUPPORT_STANDARD_PREDEFINES"));
|
|
definedlist.add(_T("NSIS_SUPPORT_NAMED_USERVARS"));
|
|
definedlist.add(_T("NSIS_SUPPORT_LANG_IN_STRINGS"));
|
|
|
|
#ifdef _WIN32
|
|
definedlist.add(_T("NSIS_WIN32_MAKENSIS"));
|
|
#endif
|
|
#ifdef _UNICODE
|
|
definedlist.add(_T("NSIS_UNICODE_MAKENSIS")); // This define might go away once makensis.exe is always unicode
|
|
#endif
|
|
|
|
db_opt_save=db_comp_save=db_full_size=db_opt_save_u=db_comp_save_u=db_full_size_u=0;
|
|
|
|
// Added by Amir Szekely 31st July 2002
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
compressor = &zlib_compressor;
|
|
#endif
|
|
build_compressor_set = false;
|
|
build_compressor_final = false;
|
|
build_compress_whole = false;
|
|
build_compress=1;
|
|
build_compress_level=9;
|
|
build_compress_dict_size=1<<23;
|
|
|
|
cur_entries=&build_entries;
|
|
cur_instruction_entry_map=&build_instruction_entry_map;
|
|
cur_datablock=&build_datablock;
|
|
cur_datablock_cache=&build_datablock_cache;
|
|
cur_functions=&build_functions;
|
|
cur_labels=&build_labels;
|
|
cur_sections=&build_sections;
|
|
cur_header=&build_header;
|
|
cur_strlist=&build_strlist;
|
|
cur_langtables=&build_langtables;
|
|
cur_ctlcolors=&build_ctlcolors;
|
|
cur_pages=&build_pages;
|
|
cur_page=0;
|
|
cur_page_type=-1;
|
|
|
|
build_filebuflen=32<<20; // 32mb
|
|
|
|
sectiongroup_open_cnt=0;
|
|
build_cursection_isfunc=0;
|
|
build_cursection=NULL;
|
|
// init public data.
|
|
build_packname[0]=build_packcmd[0]=build_output_filename[0]=0;
|
|
postbuild_cmds=NULL;
|
|
|
|
// Added by ramon 23 May 2003
|
|
build_allowskipfiles=1;
|
|
|
|
// Added by ramon 6 jun 2003
|
|
#ifdef NSIS_SUPPORT_VERSION_INFO
|
|
version_fixedflags=0;
|
|
#endif
|
|
|
|
build_overwrite=build_last_overwrite=0;
|
|
build_crcchk=1;
|
|
build_datesave=1;
|
|
build_optimize_datablock=1;
|
|
|
|
memset(&build_header,-1,sizeof(build_header));
|
|
|
|
build_header.install_reg_rootkey=0;
|
|
build_header.flags=CH_FLAGS_NO_ROOT_DIR;
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
build_header.lb_bg=RGB(0,0,0);
|
|
build_header.lb_fg=RGB(0,255,0);
|
|
#endif
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
build_header.license_bg=-COLOR_BTNFACE;
|
|
#endif
|
|
build_header.install_directory_ptr=0;
|
|
build_header.install_directory_auto_append=0;
|
|
build_header.install_reg_key_ptr=0;
|
|
build_header.install_reg_value_ptr=0;
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
memset(build_header.install_types,0,sizeof(build_header.install_types));
|
|
#endif
|
|
memset(&build_header.blocks,0,sizeof(build_header.blocks));
|
|
|
|
uninstall_mode=0;
|
|
uninstall_size_full=0;
|
|
uninstall_size=-1;
|
|
|
|
memset(&build_uninst,-1,sizeof(build_uninst));
|
|
|
|
build_header.install_reg_rootkey=0;
|
|
build_uninst.flags=0;
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
build_uninst.lb_bg=RGB(0,0,0);
|
|
build_uninst.lb_fg=RGB(0,255,0);
|
|
#endif
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
build_uninst.license_bg=-COLOR_BTNFACE;
|
|
#endif
|
|
build_uninst.install_directory_ptr=0;
|
|
build_uninst.install_directory_auto_append=0;
|
|
build_uninst.install_reg_key_ptr=0;
|
|
build_uninst.install_reg_value_ptr=0;
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
memset(build_uninst.install_types,0,sizeof(build_uninst.install_types));
|
|
#endif
|
|
memset(&build_uninst.blocks,0,sizeof(build_uninst.blocks));
|
|
|
|
uninstaller_writes_used=0;
|
|
|
|
build_strlist.add(_T(""), CP_ACP, false, build_unicode);
|
|
ubuild_strlist.add(_T(""), CP_ACP, false, build_unicode);
|
|
|
|
build_langstring_num=0;
|
|
ubuild_langstring_num=0;
|
|
|
|
build_font[0]=0;
|
|
build_font_size=0;
|
|
|
|
m_unicon_size=0;
|
|
|
|
branding_image_found=false;
|
|
|
|
no_space_texts=false;
|
|
|
|
m_currentmacroname=NULL;
|
|
|
|
#ifdef NSIS_CONFIG_PLUGIN_SUPPORT
|
|
build_plugin_unload=0;
|
|
m_pPlugins=0;
|
|
#endif
|
|
|
|
last_used_lang=NSIS_DEFAULT_LANG;
|
|
|
|
res_editor=0;
|
|
|
|
manifest_comctl = manifest::comctl_old;
|
|
manifest_exec_level = manifest::exec_level_none;
|
|
manifest_dpiaware = manifest::dpiaware_notset;
|
|
manifest_sosl.setdefault();
|
|
|
|
enable_last_page_cancel=0;
|
|
uenable_last_page_cancel=0;
|
|
|
|
license_res_id=IDD_LICENSE;
|
|
|
|
disable_window_icon=0;
|
|
|
|
notify_hwnd=0;
|
|
|
|
#ifdef NSIS_SUPPORT_BGBG
|
|
bg_default_font.lfHeight=40;
|
|
bg_default_font.lfWidth=0;
|
|
bg_default_font.lfEscapement=0;
|
|
bg_default_font.lfOrientation=0;
|
|
bg_default_font.lfWeight=FW_BOLD;
|
|
bg_default_font.lfItalic=TRUE;
|
|
bg_default_font.lfUnderline=FALSE;
|
|
bg_default_font.lfStrikeOut=FALSE;
|
|
bg_default_font.lfCharSet=DEFAULT_CHARSET;
|
|
bg_default_font.lfOutPrecision=OUT_DEFAULT_PRECIS;
|
|
bg_default_font.lfClipPrecision=CLIP_DEFAULT_PRECIS;
|
|
bg_default_font.lfQuality=DEFAULT_QUALITY;
|
|
bg_default_font.lfPitchAndFamily=DEFAULT_PITCH;
|
|
_tcsnccpy(bg_default_font.lfFaceName,_T("Times New Roman"),LF_FACESIZE);
|
|
memcpy(&bg_font,&bg_default_font,sizeof(LOGFONT));
|
|
#endif
|
|
|
|
defcodepage_set=false;
|
|
uDefCodePage=CP_ACP;
|
|
|
|
InitLangTables();
|
|
|
|
// Register static user variables $0, $1 and so on
|
|
// with ONE of reference count, to avoid warning on this vars
|
|
TCHAR Aux[3];
|
|
int i;
|
|
for (i = 0; i < 10; i++) // 0 - 9
|
|
{
|
|
wsprintf(Aux, _T("%d"), i);
|
|
m_UserVarNames.add(Aux,1);
|
|
}
|
|
for (i = 0; i < 10; i++) // 10 - 19
|
|
{
|
|
wsprintf(Aux, _T("R%d"), i);
|
|
m_UserVarNames.add(Aux,1);
|
|
}
|
|
m_UserVarNames.add(_T("CMDLINE"),1); // 20 everything before here doesn't have trailing slash removal
|
|
m_UserVarNames.add(_T("INSTDIR"),1); // 21
|
|
m_UserVarNames.add(_T("OUTDIR"),1); // 22
|
|
m_UserVarNames.add(_T("EXEDIR"),1); // 23
|
|
m_UserVarNames.add(_T("LANGUAGE"),1); // 24
|
|
m_UserVarNames.add(_T("TEMP"),-1); // 25
|
|
m_UserVarNames.add(_T("PLUGINSDIR"),-1); // 26
|
|
m_UserVarNames.add(_T("EXEPATH"),-1); // 27
|
|
m_UserVarNames.add(_T("EXEFILE"),-1); // 28
|
|
m_UserVarNames.add(_T("HWNDPARENT"),-1); // 29
|
|
m_UserVarNames.add(_T("_CLICK"),-1); // 30
|
|
m_UserVarNames.add(_T("_OUTDIR"),1); // 31
|
|
|
|
m_iBaseVarsNum = m_UserVarNames.getnum();
|
|
|
|
m_ShellConstants.add(_T("WINDIR"),CSIDL_WINDOWS,CSIDL_WINDOWS);
|
|
m_ShellConstants.add(_T("SYSDIR"),CSIDL_SYSTEM,CSIDL_SYSTEM);
|
|
m_ShellConstants.add(_T("SMPROGRAMS"),CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS);
|
|
m_ShellConstants.add(_T("SMSTARTUP"),CSIDL_STARTUP, CSIDL_COMMON_STARTUP);
|
|
m_ShellConstants.add(_T("DESKTOP"),CSIDL_DESKTOPDIRECTORY, CSIDL_COMMON_DESKTOPDIRECTORY);
|
|
m_ShellConstants.add(_T("STARTMENU"),CSIDL_STARTMENU, CSIDL_COMMON_STARTMENU);
|
|
m_ShellConstants.add(_T("QUICKLAUNCH"), CSIDL_APPDATA, CSIDL_APPDATA);
|
|
m_ShellConstants.add(_T("DOCUMENTS"),CSIDL_PERSONAL, CSIDL_COMMON_DOCUMENTS);
|
|
m_ShellConstants.add(_T("SENDTO"),CSIDL_SENDTO, CSIDL_SENDTO);
|
|
m_ShellConstants.add(_T("RECENT"),CSIDL_RECENT, CSIDL_RECENT);
|
|
m_ShellConstants.add(_T("FAVORITES"),CSIDL_FAVORITES, CSIDL_COMMON_FAVORITES);
|
|
m_ShellConstants.add(_T("MUSIC"),CSIDL_MYMUSIC, CSIDL_COMMON_MUSIC);
|
|
m_ShellConstants.add(_T("PICTURES"),CSIDL_MYPICTURES, CSIDL_COMMON_PICTURES);
|
|
m_ShellConstants.add(_T("VIDEOS"),CSIDL_MYVIDEO, CSIDL_COMMON_VIDEO);
|
|
m_ShellConstants.add(_T("NETHOOD"), CSIDL_NETHOOD, CSIDL_NETHOOD);
|
|
m_ShellConstants.add(_T("FONTS"), CSIDL_FONTS, CSIDL_FONTS);
|
|
m_ShellConstants.add(_T("TEMPLATES"), CSIDL_TEMPLATES, CSIDL_COMMON_TEMPLATES);
|
|
m_ShellConstants.add(_T("APPDATA"), CSIDL_APPDATA, CSIDL_COMMON_APPDATA);
|
|
m_ShellConstants.add(_T("LOCALAPPDATA"), CSIDL_LOCAL_APPDATA, CSIDL_LOCAL_APPDATA);
|
|
m_ShellConstants.add(_T("PRINTHOOD"), CSIDL_PRINTHOOD, CSIDL_PRINTHOOD);
|
|
//m_ShellConstants.add(_T("ALTSTARTUP"), CSIDL_ALTSTARTUP, CSIDL_COMMON_ALTSTARTUP);
|
|
m_ShellConstants.add(_T("INTERNET_CACHE"), CSIDL_INTERNET_CACHE, CSIDL_INTERNET_CACHE);
|
|
m_ShellConstants.add(_T("COOKIES"), CSIDL_COOKIES, CSIDL_COOKIES);
|
|
m_ShellConstants.add(_T("HISTORY"), CSIDL_HISTORY, CSIDL_HISTORY);
|
|
m_ShellConstants.add(_T("PROFILE"), CSIDL_PROFILE, CSIDL_PROFILE);
|
|
m_ShellConstants.add(_T("ADMINTOOLS"), CSIDL_ADMINTOOLS, CSIDL_COMMON_ADMINTOOLS);
|
|
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("CDBURN_AREA"), CSIDL_CDBURN_AREA, CSIDL_CDBURN_AREA);
|
|
|
|
unsigned int program_files = add_string(_T("ProgramFilesDir"), 0);
|
|
unsigned int program_files_def = add_string(_T("C:\\Program Files"));
|
|
|
|
if ((program_files >= 0x40) || (program_files_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!");
|
|
}
|
|
|
|
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_code_type_predefines();
|
|
|
|
set_target_architecture_predefines();
|
|
}
|
|
|
|
void CEXEBuild::initialize(const TCHAR *makensis_path)
|
|
{
|
|
tstring nsis_dir;
|
|
const TCHAR *dir = _tgetenv(_T("NSISDIR"));
|
|
if (dir) nsis_dir = dir;
|
|
else {
|
|
#ifndef NSIS_CONFIG_CONST_DATA_PATH
|
|
nsis_dir = get_dir_name(get_executable_dir(makensis_path));
|
|
#else
|
|
nsis_dir = PREFIX_DATA;
|
|
#endif
|
|
}
|
|
definedlist.add(_T("NSISDIR"), nsis_dir.c_str());
|
|
|
|
tstring includes_dir = nsis_dir;
|
|
includes_dir += PLATFORM_PATH_SEPARATOR_STR _T("Include");
|
|
include_dirs.add(includes_dir.c_str(),0);
|
|
|
|
stubs_dir = nsis_dir;
|
|
stubs_dir += PLATFORM_PATH_SEPARATOR_STR _T("Stubs");
|
|
|
|
if (set_compressor(_T("zlib"), false) != PS_OK)
|
|
{
|
|
throw runtime_error("error setting default stub");
|
|
}
|
|
|
|
tstring uninst = stubs_dir + PLATFORM_PATH_SEPARATOR_STR + _T("uninst");
|
|
uninstaller_icon = load_icon_file(uninst.c_str());
|
|
}
|
|
|
|
|
|
int CEXEBuild::getcurdbsize() { return cur_datablock->getlen(); }
|
|
|
|
// returns offset in stringblock
|
|
int CEXEBuild::add_string(const TCHAR *string, int process/*=1*/, WORD codepage/*=CP_ACP*/)
|
|
{
|
|
if (!string || !*string) return 0;
|
|
|
|
if (*string == _T('$') && *(string+1) == _T('(')) {
|
|
int idx = 0;
|
|
TCHAR *cp = _tcsdup(string+2);
|
|
TCHAR *p = _tcschr(cp, _T(')'));
|
|
if (p && p[1] == _T('\0') ) { // if string is only a language str identifier
|
|
*p = 0;
|
|
idx = DefineLangString(cp, process);
|
|
}
|
|
free(cp);
|
|
if (idx < 0) return idx;
|
|
}
|
|
|
|
if (!process) return cur_strlist->add(string, codepage, false, build_unicode);
|
|
|
|
TCHAR buf[NSIS_MAX_STRLEN*4];
|
|
preprocess_string(buf,string,codepage);
|
|
return cur_strlist->add(buf,codepage, true, build_unicode);
|
|
}
|
|
|
|
int CEXEBuild::add_intstring(const int i) // returns offset in stringblock
|
|
{
|
|
TCHAR i_str[32];
|
|
wsprintf(i_str, _T("%d"), i);
|
|
return add_string(i_str);
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
char* convert_processed_string_to_ansi(char *out, const TCHAR *in, WORD codepage)
|
|
{
|
|
const TCHAR *p=in;
|
|
for (;;)
|
|
{
|
|
_TUCHAR i = (_TUCHAR)*p++;
|
|
if (NS_IS_CODE(i)) // Note: this includes '\0'
|
|
{
|
|
// convert all character up to, and including this code
|
|
int cb = WideCharToMultiByte(codepage, 0, in, p-in, out, (p-in)*2, NULL, NULL);
|
|
out += cb;
|
|
if (i == _T('\0'))
|
|
break;
|
|
else if (i == NS_SKIP_CODE)
|
|
*out++ = (char) *in++; // simply copy escaped code (01..04)
|
|
else
|
|
{
|
|
WORD w = *p++; // special NSIS code is following by a WORD we need to output unchanged
|
|
*out++ = LOBYTE(w);
|
|
*out++ = HIBYTE(w);
|
|
}
|
|
in = p;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
#endif
|
|
|
|
// based on Dave Laundon's code
|
|
int CEXEBuild::preprocess_string(TCHAR *out, const TCHAR *in, WORD codepage/*=CP_ACP*/)
|
|
{
|
|
const TCHAR *p=in;
|
|
while (*p)
|
|
{
|
|
const TCHAR *np;
|
|
#ifdef _UNICODE
|
|
np = CharNext(p);
|
|
#else
|
|
np = CharNextExA(codepage, p, 0);
|
|
#endif
|
|
if (np - p > 1) // multibyte TCHAR
|
|
{
|
|
int l = np - p;
|
|
while (l--)
|
|
{
|
|
_TUCHAR i = (_TUCHAR)*p++;
|
|
if (NS_IS_CODE(i)) {
|
|
*out++ = (TCHAR)NS_SKIP_CODE;
|
|
}
|
|
*out++=(TCHAR)i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
_TUCHAR i = (_TUCHAR)*p;
|
|
|
|
p=np; // increment p.
|
|
|
|
// Test for characters extending into the variable codes
|
|
if (NS_IS_CODE(i)) {
|
|
*out++ = (TCHAR)NS_SKIP_CODE;
|
|
// out does get the NS_CODE as well because of
|
|
// "*out++=(TCHAR)i" at the end.
|
|
}
|
|
else if (i == _T('$'))
|
|
{
|
|
if (*p == _T('$'))
|
|
p++; // Can simply convert $$ to $ now
|
|
else
|
|
{
|
|
// starts with a $ but not $$.
|
|
{ // block - why do we need this extra {?
|
|
bool bProceced=false;
|
|
if ( *p )
|
|
{
|
|
const TCHAR *pUserVarName = p;
|
|
while (isSimpleChar(*pUserVarName))
|
|
pUserVarName++;
|
|
|
|
while (pUserVarName > p)
|
|
{
|
|
if (m_ShellConstants.get((TCHAR*)p, pUserVarName-p) >= 0)
|
|
break; // Woops it's a shell constant
|
|
|
|
// Jim Park: The following line could be a source of bugs for
|
|
// variables where one variable name is a prefix of another
|
|
// variable name. For example, if you are searching for
|
|
// variable 'UserVar', you can get 'UserVariable' instead.
|
|
// Suggest that we do:
|
|
// TCHAR varname[NSIS_MAX_STRLEN];
|
|
// _tcsncpy(varname, p, pUserVarName-p);
|
|
// int idxUserVar = m_UserVarNames.get(varname);
|
|
int idxUserVar = m_UserVarNames.get((TCHAR*)p, pUserVarName-p);
|
|
if (idxUserVar >= 0)
|
|
{
|
|
// Well, using variables inside string formating doens't mean
|
|
// using the variable, beacuse it will be always an empty string
|
|
// which is also memory wasting
|
|
// So the line below must be commented !??
|
|
//m_UserVarNames.inc_reference(idxUserVar);
|
|
*out++ = (TCHAR) NS_VAR_CODE; // Named user variable;
|
|
WORD w = FIX_ENDIAN_INT16(CODE_SHORT(idxUserVar));
|
|
memcpy(out, &w, sizeof(WORD));
|
|
out += sizeof(WORD)/sizeof(TCHAR);
|
|
p += pUserVarName-p; // zip past the user var string.
|
|
bProceced = true;
|
|
break;
|
|
}
|
|
pUserVarName--;
|
|
}
|
|
}// if ( *p )
|
|
if (!bProceced && *p)
|
|
{
|
|
const TCHAR *pShellConstName = p;
|
|
while (isSimpleChar(*pShellConstName))
|
|
pShellConstName++;
|
|
|
|
while (pShellConstName > p)
|
|
{
|
|
// Look for the identifier in the shell constants list of strings.
|
|
int idxConst = m_ShellConstants.get((TCHAR*)p, pShellConstName - p);
|
|
|
|
// If found...
|
|
if (idxConst >= 0)
|
|
{
|
|
int CSIDL_Value_current = m_ShellConstants.get_value1(idxConst);
|
|
int CSIDL_Value_all = m_ShellConstants.get_value2(idxConst);
|
|
*out++=(TCHAR)NS_SHELL_CODE; // Constant code identifier
|
|
#ifdef _UNICODE
|
|
*out++=MAKEWORD(CSIDL_Value_current, CSIDL_Value_all);
|
|
#else
|
|
*out++=(TCHAR)CSIDL_Value_current;
|
|
*out++=(TCHAR)CSIDL_Value_all;
|
|
#endif
|
|
p = pShellConstName; // zip past the shell constant string.
|
|
bProceced = true;
|
|
break;
|
|
}
|
|
|
|
// We are looking from the longest identifier first and work
|
|
// smaller.
|
|
pShellConstName--;
|
|
}
|
|
}
|
|
if ( !bProceced && *p == _T('(') )
|
|
{
|
|
int idx = -1;
|
|
TCHAR *cp = _tcsdup(p+1); // JP: Bad... should avoid memory alloc.
|
|
TCHAR *pos = _tcschr(cp, _T(')'));
|
|
if (pos)
|
|
{
|
|
*pos = 0;
|
|
idx = DefineLangString(cp);
|
|
if (idx < 0)
|
|
{
|
|
*out++ = (TCHAR)NS_LANG_CODE; // Next word is lang-string Identifier
|
|
WORD w = FIX_ENDIAN_INT16(CODE_SHORT(-idx-1));
|
|
memcpy(out, &w, sizeof(WORD));
|
|
out += sizeof(WORD)/sizeof(TCHAR);
|
|
p += _tcslen(cp) + 2;
|
|
bProceced = true;
|
|
}
|
|
}
|
|
free(cp);
|
|
}
|
|
if ( bProceced )
|
|
continue; // outermost while
|
|
else
|
|
{
|
|
TCHAR tbuf[64];
|
|
TCHAR cBracket = _T('\0');
|
|
bool bDoWarning = true;
|
|
|
|
if ( *p == _T('[') )
|
|
cBracket = _T(']');
|
|
else if ( *p == _T('(') )
|
|
cBracket = _T(')');
|
|
else if ( *p == _T('{') )
|
|
cBracket = _T('}');
|
|
|
|
_tcsnccpy(tbuf,p,63);
|
|
tbuf[63]=0;
|
|
|
|
if ( cBracket != 0 )
|
|
{
|
|
if (_tcschr(tbuf,cBracket)) (_tcschr(tbuf,cBracket)+1)[0]=0;
|
|
if ( tbuf[0] == _T('{') && tbuf[_tcslen(tbuf)-1] == _T('}') )
|
|
{
|
|
TCHAR *tstIfDefine = _tcsdup(tbuf+1);
|
|
tstIfDefine[_tcslen(tstIfDefine)-1] = _T('\0');
|
|
bDoWarning = definedlist.find(tstIfDefine) == NULL;
|
|
// If it's a defined identifier, then don't warn.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_tcsstr(tbuf,_T(" "))) _tcsstr(tbuf,_T(" "))[0]=0;
|
|
}
|
|
if ( bDoWarning )
|
|
warning_fl(_T("unknown variable/constant \"%s\" detected, ignoring"),tbuf);
|
|
i = _T('$'); // redundant since i is already '$' and has
|
|
// not changed.
|
|
}
|
|
} // block
|
|
} // else
|
|
} // else if (i == _T('$'))
|
|
|
|
*out++=(TCHAR)i;
|
|
} // outside while
|
|
*out=0;
|
|
return 0;
|
|
}
|
|
|
|
// what it does is, when you pass it the offset of the last item added, it will determine if
|
|
// that data is already present in the datablock, and if so, reference it instead (and shorten
|
|
// the datablock as necessary). Reduces overhead if you want to add files to a couple places.
|
|
// Woo, an optimizing installer generator, now we're styling.
|
|
|
|
int CEXEBuild::datablock_optimize(int start_offset, int first_int)
|
|
{
|
|
int this_len = cur_datablock->getlen() - start_offset;
|
|
|
|
cached_db_size this_size = {first_int, start_offset};
|
|
this->cur_datablock_cache->add(&this_size, sizeof(cached_db_size));
|
|
|
|
if (!this->build_optimize_datablock || this_len < (int) sizeof(int))
|
|
return start_offset;
|
|
|
|
MMapBuf *db = (MMapBuf *) cur_datablock;
|
|
db->setro(TRUE);
|
|
|
|
cached_db_size *db_sizes = (cached_db_size *) this->cur_datablock_cache->get();
|
|
int db_sizes_num = this->cur_datablock_cache->getlen() / sizeof(cached_db_size);
|
|
db_sizes_num--; // don't compare with the one we just added
|
|
|
|
for (int i = 0; i < db_sizes_num; i++)
|
|
{
|
|
if (db_sizes[i].first_int == first_int)
|
|
{
|
|
int pos = db_sizes[i].start_offset;
|
|
int left = this_len;
|
|
while (left > 0)
|
|
{
|
|
int l = min(left, build_filebuflen);
|
|
void *newstuff = db->get(start_offset + this_len - left, l);
|
|
void *oldstuff = db->getmore(pos + this_len - left, l);
|
|
|
|
int res = memcmp(newstuff, oldstuff, l);
|
|
|
|
db->release(oldstuff, l);
|
|
db->release();
|
|
|
|
if (res)
|
|
{
|
|
break;
|
|
}
|
|
|
|
left -= l;
|
|
}
|
|
|
|
if (!left)
|
|
{
|
|
db_opt_save += this_len;
|
|
db->resize(max(start_offset, pos + this_len));
|
|
db->setro(FALSE);
|
|
this->cur_datablock_cache->resize(cur_datablock_cache->getlen() - sizeof(cached_db_size));
|
|
return pos;
|
|
}
|
|
}
|
|
}
|
|
|
|
db->setro(FALSE);
|
|
|
|
return start_offset;
|
|
}
|
|
|
|
int CEXEBuild::add_db_data(IMMap *mmap) // returns offset
|
|
{
|
|
build_compressor_set = true;
|
|
|
|
int done = 0;
|
|
|
|
if (!mmap)
|
|
{
|
|
ERROR_MSG(_T("Error: add_db_data() called with invalid mapped file\n"));
|
|
return -1;
|
|
}
|
|
|
|
int length = mmap->getsize();
|
|
|
|
if (length < 0)
|
|
{
|
|
ERROR_MSG(_T("Error: add_db_data() called with length=%d\n"), length);
|
|
return -1;
|
|
}
|
|
|
|
// Jim Park: This kind of stuff looks scary and it is. cur_datablock is
|
|
// most likely to point to a MMapBuf type right now so it works.
|
|
MMapBuf *db = (MMapBuf *) this->cur_datablock;
|
|
|
|
int st = db->getlen();
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (length && !build_compress_whole && build_compress)
|
|
{
|
|
// grow datablock so that there is room to compress into
|
|
int bufferlen = length + 1024 + length / 4; // give a nice 25% extra space
|
|
if (st+bufferlen+sizeof(int) < 0) // we've hit a signed integer overflow (file is over 1.6 GB)
|
|
bufferlen = INT_MAX-st-sizeof(int); // so maximize compressor room and hope the file compresses well
|
|
db->resize(st + bufferlen + sizeof(int));
|
|
|
|
int n = compressor->Init(build_compress_level, build_compress_dict_size);
|
|
if (n != C_OK)
|
|
{
|
|
ERROR_MSG(_T("Internal compiler error #12345: deflateInit() failed(%s [%d]).\n"), compressor->GetErrStr(n), n);
|
|
extern void quit(); quit();
|
|
}
|
|
|
|
int avail_in = length;
|
|
int avail_out = bufferlen;
|
|
int ret;
|
|
while (avail_in > 0)
|
|
{
|
|
int in_len = min(this->build_filebuflen, avail_in);
|
|
int out_len = min(this->build_filebuflen, avail_out);
|
|
|
|
compressor->SetNextIn((char*) mmap->get(length - avail_in, in_len), in_len);
|
|
compressor->SetNextOut((char*) db->get(st + sizeof(int) + bufferlen - avail_out, out_len), out_len);
|
|
if ((ret = compressor->Compress(0)) < 0)
|
|
{
|
|
ERROR_MSG(_T("Error: add_db_data() - compress() failed(%s [%d])\n"), compressor->GetErrStr(ret), ret);
|
|
return -1;
|
|
}
|
|
mmap->release();
|
|
db->flush(out_len);
|
|
db->release();
|
|
avail_in -= in_len - compressor->GetAvailIn();
|
|
avail_out -= out_len - compressor->GetAvailOut();
|
|
|
|
if (!avail_out)
|
|
// not enough space in the output buffer - no compression is better
|
|
break;
|
|
}
|
|
|
|
// if not enough space in the output buffer - no compression is better
|
|
if (avail_out)
|
|
{
|
|
char *out;
|
|
|
|
char a;
|
|
compressor->SetNextIn(&a,0);
|
|
|
|
do
|
|
{
|
|
int out_len = min(build_filebuflen, avail_out);
|
|
|
|
out = (char *) db->get(st + sizeof(int) + bufferlen - avail_out, out_len);
|
|
|
|
compressor->SetNextOut(out, out_len);
|
|
if ((ret = compressor->Compress(C_FINISH)) < 0)
|
|
{
|
|
ERROR_MSG(_T("Error: add_db_data() - compress() failed(%s [%d])\n"), compressor->GetErrStr(ret), ret);
|
|
return -1;
|
|
}
|
|
|
|
db->flush(out_len);
|
|
db->release();
|
|
|
|
avail_out -= out_len - compressor->GetAvailOut();
|
|
}
|
|
while (compressor->GetNextOut() - out > 0 && avail_out > 0);
|
|
|
|
compressor->End();
|
|
|
|
int used = bufferlen - avail_out;
|
|
|
|
// never store compressed if output buffer is full (compression increased the size...)
|
|
if (avail_out && (build_compress == 2 || used < length))
|
|
{
|
|
done=1;
|
|
db->resize(st + used + sizeof(int));
|
|
|
|
*(int*)db->get(st, sizeof(int)) = FIX_ENDIAN_INT32(used | 0x80000000);
|
|
db->release();
|
|
|
|
int nst = datablock_optimize(st, used | 0x80000000);
|
|
if (nst == st) db_comp_save += length - used;
|
|
else st = nst;
|
|
}
|
|
}
|
|
else
|
|
compressor->End();
|
|
}
|
|
#endif // NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
|
|
if (!done)
|
|
{
|
|
db->resize(st + length + sizeof(int));
|
|
int *plen = (int *) db->get(st, sizeof(int));
|
|
*plen = FIX_ENDIAN_INT32(length);
|
|
db->release();
|
|
|
|
int left = length;
|
|
while (left > 0)
|
|
{
|
|
int l = min(build_filebuflen, left);
|
|
int *p = (int *) db->get(st + sizeof(int) + length - left, l);
|
|
memcpy(p, mmap->get(length - left, l), l);
|
|
db->flush(l);
|
|
db->release();
|
|
mmap->release();
|
|
left -= l;
|
|
}
|
|
|
|
st = datablock_optimize(st, length);
|
|
}
|
|
|
|
db_full_size += length + sizeof(int);
|
|
|
|
return st;
|
|
}
|
|
|
|
int CEXEBuild::add_db_data(const char *data, int length) // returns offset
|
|
{
|
|
MMapFake fakemap;
|
|
fakemap.set(data, length);
|
|
return add_db_data(&fakemap);
|
|
}
|
|
|
|
int CEXEBuild::add_data(const char *data, int length, IGrowBuf *dblock) // returns offset
|
|
{
|
|
build_compressor_set=true;
|
|
|
|
int done=0;
|
|
|
|
if (length < 0)
|
|
{
|
|
ERROR_MSG(_T("Error: add_data() called with length=%d\n"),length);
|
|
return -1;
|
|
}
|
|
|
|
int st=dblock->getlen();
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (!build_compress_whole && build_compress)
|
|
{
|
|
// grow datablock so that there is room to compress into
|
|
int bufferlen=length+1024+length/4; // give a nice 25% extra space
|
|
dblock->resize(st+bufferlen+sizeof(int));
|
|
|
|
int n = compressor->Init(build_compress_level, build_compress_dict_size);
|
|
if (n != C_OK)
|
|
{
|
|
ERROR_MSG(_T("Internal compiler error #12345: deflateInit() failed(%s [%d]).\n"), compressor->GetErrStr(n), n);
|
|
extern void quit(); quit();
|
|
}
|
|
|
|
compressor->SetNextIn((char*)data, length);
|
|
compressor->SetNextOut((char*)dblock->get() + st + sizeof(int), bufferlen);
|
|
|
|
compressor->Compress(C_FINISH);
|
|
|
|
int used=bufferlen-compressor->GetAvailOut();
|
|
|
|
// never store compressed if output buffer is full
|
|
if (compressor->GetAvailOut() && (build_compress == 2 || used < length))
|
|
{
|
|
done=1;
|
|
dblock->resize(st+used+sizeof(int));
|
|
|
|
*((int*)((char *)dblock->get()+st)) = FIX_ENDIAN_INT32(used|0x80000000);
|
|
}
|
|
compressor->End();
|
|
}
|
|
#endif // NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
|
|
if (!done)
|
|
{
|
|
dblock->resize(st);
|
|
int rl = FIX_ENDIAN_INT32(length);
|
|
dblock->add(&rl,sizeof(int));
|
|
dblock->add(data,length);
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
int CEXEBuild::add_label(const TCHAR *name)
|
|
{
|
|
if (!build_cursection)
|
|
{
|
|
ERROR_MSG(_T("Error: Label declaration not valid outside of function/section\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if ((name[0] >= _T('0') && name[0] <= _T('9')) || name[0] == _T('-') || name[0] == _T(' ') || name[0] == _T(':'))
|
|
{
|
|
ERROR_MSG(_T("Error: labels must not begin with 0-9, -, :, or a space.\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
int cs=build_cursection->code;
|
|
int ce=cs+build_cursection->code_size;
|
|
|
|
TCHAR *p=_tcsdup(name);
|
|
if (p[_tcslen(p)-1] == _T(':')) p[_tcslen(p)-1]=0;
|
|
int offs=ns_label.add(p,0);
|
|
free(p);
|
|
|
|
int n=cur_labels->getlen()/sizeof(section);
|
|
|
|
// Check to see if the label already exists.
|
|
if (n)
|
|
{
|
|
section *t=(section*)cur_labels->get();
|
|
while (n--)
|
|
{
|
|
// Labels beginning with '.' are global and can be jumped to from any function or section.
|
|
if ((*name == _T('.') || (t->code >= cs && t->code <= ce)) &&
|
|
t->name_ptr==offs)
|
|
{
|
|
if (*name == _T('.')) ERROR_MSG(_T("Error: global label \"%s\" already declared\n"),name);
|
|
else
|
|
{
|
|
const TCHAR *szType = _T("section");
|
|
if (build_cursection_isfunc)
|
|
szType = _T("function");
|
|
ERROR_MSG(_T("Error: label \"%s\" already declared in %s\n"),name,szType);
|
|
}
|
|
return PS_ERROR;
|
|
}
|
|
t++;
|
|
}
|
|
}
|
|
|
|
section s={0};
|
|
s.name_ptr = offs;
|
|
s.code = ce;
|
|
cur_labels->add(&s,sizeof(s));
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::add_function(const TCHAR *funname)
|
|
{
|
|
if (build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: Function open when creating function (use FunctionEnd first)\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (build_cursection)
|
|
{
|
|
ERROR_MSG(_T("Error: Section open when creating function (use SectionEnd first)\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (cur_page)
|
|
{
|
|
ERROR_MSG(_T("Error: PageEx open when creating function (use PageExEnd first)\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (!funname[0])
|
|
{
|
|
ERROR_MSG(_T("Error: Function must have a name\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
set_uninstall_mode(!_tcsnicmp(funname,_T("un."),3));
|
|
|
|
// ns_func contains all the function names defined.
|
|
int addr=ns_func.add(funname,0);
|
|
int x;
|
|
int n=cur_functions->getlen()/sizeof(section);
|
|
section *tmp=(section*)cur_functions->get();
|
|
for (x = 0; x < n; x ++)
|
|
{
|
|
if (tmp[x].name_ptr == addr)
|
|
{
|
|
ERROR_MSG(_T("Error: Function named \"%s\" already exists.\n"),funname);
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
|
|
cur_functions->resize((n+1)*sizeof(section));
|
|
build_cursection=((section*)cur_functions->get())+n;
|
|
build_cursection_isfunc=1;
|
|
build_cursection->name_ptr=addr;
|
|
build_cursection->code=cur_entries->getlen()/sizeof(entry);
|
|
build_cursection->code_size=0;
|
|
build_cursection->install_types=0;
|
|
build_cursection->flags=0;
|
|
build_cursection->size_kb=0;
|
|
memset(build_cursection->name,0,sizeof(build_cursection->name));
|
|
|
|
if (uninstall_mode)
|
|
set_code_type_predefines(funname+3);
|
|
else
|
|
set_code_type_predefines(funname);
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::function_end()
|
|
{
|
|
if (!build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: No function open, FunctionEnd called\n"));
|
|
return PS_ERROR;
|
|
}
|
|
// add ret.
|
|
add_entry_direct(EW_RET);
|
|
|
|
build_cursection_isfunc=0;
|
|
build_cursection=NULL;
|
|
|
|
set_uninstall_mode(0);
|
|
|
|
set_code_type_predefines();
|
|
return PS_OK;
|
|
}
|
|
|
|
|
|
int CEXEBuild::section_add_flags(int flags)
|
|
{
|
|
if (!build_cursection || build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: can't modify flags when no section is open\n"));
|
|
return PS_ERROR;
|
|
}
|
|
build_cursection->flags |= flags;
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::section_add_install_type(int inst_type)
|
|
{
|
|
if (!build_cursection || build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: can't modify flags when no section is open\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (build_cursection->install_types == ~0)
|
|
build_cursection->install_types = 0;
|
|
build_cursection->install_types |= inst_type;
|
|
return PS_OK;
|
|
}
|
|
|
|
void CEXEBuild::section_add_size_kb(int kb)
|
|
{
|
|
if (build_cursection)
|
|
{
|
|
build_cursection->size_kb+=kb;
|
|
}
|
|
}
|
|
|
|
int CEXEBuild::section_end()
|
|
{
|
|
if (build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: SectionEnd specified in function (not section)\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (!build_cursection)
|
|
{
|
|
ERROR_MSG(_T("Error: SectionEnd specified and no sections open\n"));
|
|
return PS_ERROR;
|
|
}
|
|
add_entry_direct(EW_RET);
|
|
build_cursection->code_size--;
|
|
build_cursection=NULL;
|
|
if (!sectiongroup_open_cnt)
|
|
set_uninstall_mode(0);
|
|
|
|
set_code_type_predefines();
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::add_section(const TCHAR *secname, const TCHAR *defname, int expand/*=0*/)
|
|
{
|
|
if (build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: Section can't create section (already in function, use FunctionEnd first)\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (cur_page) {
|
|
ERROR_MSG(_T("Error: PageEx already open, call PageExEnd first\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (build_cursection)
|
|
{
|
|
ERROR_MSG(_T("Error: Section already open, call SectionEnd first\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
section new_section;
|
|
new_section.flags = SF_SELECTED;
|
|
new_section.flags |= expand ? SF_EXPAND : 0;
|
|
new_section.code_size = 0;
|
|
new_section.size_kb = 0;
|
|
|
|
TCHAR *name = (TCHAR*)secname;
|
|
|
|
if (name[0] == _T('\x1F')) // SectionGroup/SectionGroupEnd
|
|
{
|
|
if (name[1])
|
|
{
|
|
new_section.flags |= SF_SECGRP;
|
|
name++;
|
|
}
|
|
else
|
|
new_section.flags |= SF_SECGRPEND;
|
|
}
|
|
|
|
int hidden = (name[0] == _T('-'));
|
|
if (hidden)
|
|
name++;
|
|
|
|
if (name[0] == _T('!'))
|
|
{
|
|
name++;
|
|
new_section.flags |= SF_BOLD;
|
|
}
|
|
|
|
int old_uninstall_mode = uninstall_mode;
|
|
|
|
set_uninstall_mode(0);
|
|
|
|
if (!_tcsnicmp(name, _T("un."), 3))
|
|
{
|
|
set_uninstall_mode(1);
|
|
name += 3;
|
|
}
|
|
|
|
if (!_tcsicmp(name, _T("uninstall")))
|
|
{
|
|
set_uninstall_mode(1);
|
|
}
|
|
|
|
if ((new_section.flags & SF_SECGRPEND) && sectiongroup_open_cnt && old_uninstall_mode)
|
|
{
|
|
set_uninstall_mode(1);
|
|
}
|
|
|
|
if (sectiongroup_open_cnt)
|
|
{
|
|
if (uninstall_mode != old_uninstall_mode)
|
|
{
|
|
ERROR_MSG(_T("Error: Can't create %s section in %s section group (use SectionGroupEnd first)\n"), uninstall_mode ? _T("uninstaller") : _T("installer"), old_uninstall_mode ? _T("uninstaller") : _T("installer"));
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
|
|
new_section.code = cur_entries->getlen() / sizeof(entry);
|
|
|
|
new_section.install_types = (!hidden && *name) ? 0 : ~0;
|
|
new_section.name_ptr = hidden ? 0 : add_string(name);
|
|
memset(&new_section.name,0,sizeof(new_section.name));
|
|
|
|
cur_sections->add(&new_section, sizeof(section));
|
|
build_cursection = (section *) cur_sections->get() + cur_header->blocks[NB_SECTIONS].num;
|
|
|
|
if (defname[0])
|
|
{
|
|
TCHAR buf[1024];
|
|
wsprintf(buf, _T("%d"), cur_header->blocks[NB_SECTIONS].num);
|
|
if (definedlist.add(defname, buf))
|
|
{
|
|
ERROR_MSG(_T("Error: \"%s\" already defined, can't assign section index!\n"), defname);
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
|
|
cur_header->blocks[NB_SECTIONS].num++;
|
|
|
|
if (new_section.flags & (SF_SECGRP | SF_SECGRPEND))
|
|
{
|
|
add_entry_direct(EW_RET);
|
|
build_cursection->code_size = 0;
|
|
|
|
build_cursection = 0;
|
|
|
|
if (new_section.flags & SF_SECGRPEND)
|
|
{
|
|
sectiongroup_open_cnt--;
|
|
if (sectiongroup_open_cnt < 0)
|
|
{
|
|
ERROR_MSG(_T("SectionGroupEnd: no SectionGroups are open\n"));
|
|
return PS_ERROR;
|
|
}
|
|
if (!sectiongroup_open_cnt)
|
|
{
|
|
set_uninstall_mode(0);
|
|
}
|
|
}
|
|
else
|
|
sectiongroup_open_cnt++;
|
|
}
|
|
|
|
set_code_type_predefines(name);
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::add_entry(const entry *ent)
|
|
{
|
|
if (!build_cursection && !uninstall_mode)
|
|
{
|
|
ERROR_MSG(_T("Error: Can't add entry, no section or function is open!\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
cur_entries->add(ent,sizeof(entry));
|
|
cur_instruction_entry_map->add(&multiple_entries_instruction,sizeof(int));
|
|
build_cursection->code_size++;
|
|
cur_header->blocks[NB_ENTRIES].num++;
|
|
|
|
multiple_entries_instruction=1;
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::add_entry_direct(int which, int o0, int o1, int o2, int o3, int o4, int o5 /*o#=0*/)
|
|
{
|
|
entry ent;
|
|
ent.which = which;
|
|
ent.offsets[0] = o0;
|
|
ent.offsets[1] = o1;
|
|
ent.offsets[2] = o2;
|
|
ent.offsets[3] = o3;
|
|
ent.offsets[4] = o4;
|
|
ent.offsets[5] = o5;
|
|
return add_entry(&ent);
|
|
}
|
|
|
|
int CEXEBuild::resolve_jump_int(const TCHAR *fn, int *a, int offs, int start, int end)
|
|
{
|
|
if (*a > 0)
|
|
{
|
|
TCHAR *lname=(TCHAR*)ns_label.get()+*a;
|
|
if (lname[0] == _T('-') || lname[0]==_T('+'))
|
|
{
|
|
int jump = _ttoi(lname);
|
|
int *skip_map = (int *) cur_instruction_entry_map->get();
|
|
int maxoffs = cur_instruction_entry_map->getlen() / (int) sizeof(int);
|
|
|
|
int direction = 1;
|
|
if (jump < 0)
|
|
direction = -1;
|
|
|
|
for (; jump != 0; jump -= direction)
|
|
{
|
|
offs += direction;
|
|
if (offs >= 0 && offs < maxoffs)
|
|
{
|
|
while (skip_map[offs])
|
|
{
|
|
offs += direction;
|
|
}
|
|
}
|
|
}
|
|
|
|
*a = offs + 1;
|
|
}
|
|
else
|
|
{
|
|
section *s = (section*)cur_labels->get();
|
|
int n=cur_labels->getlen()/sizeof(section);
|
|
while (n-->0)
|
|
{
|
|
if ((*lname == _T('.') || (s->code >= start && s->code <= end)) && s->name_ptr == *a)
|
|
{
|
|
*a = s->code+1; // jumps are to the absolute position, +1 (to differentiate between no jump, and jumping to offset 0)
|
|
s->flags++;
|
|
if (*lname == _T('.'))
|
|
{
|
|
// bug #2593369 - mark functions with used global labels as used
|
|
// XXX this puts another hole in function reference counting
|
|
// a recursive function, for example, will never be optimized
|
|
int nf=cur_functions->getlen()/sizeof(section);
|
|
section *func=(section *)cur_functions->get();
|
|
while (nf-- > 0)
|
|
{
|
|
int fstart = func->code;
|
|
int fend = func->code + func->code_size;
|
|
if (s->code >= fstart && s->code <= fend)
|
|
{
|
|
func->flags++;
|
|
break;
|
|
}
|
|
func++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
s++;
|
|
}
|
|
|
|
ERROR_MSG(_T("Error: could not resolve label \"%s\" in %s\n"),lname,fn);
|
|
return 1;
|
|
}
|
|
}
|
|
else if (*a < 0) // to jump to a user variable target, -variable_index-1 is already stored.
|
|
{
|
|
}
|
|
// otherwise, *a is 0, which means no jump and we also leave it intact
|
|
return 0;
|
|
}
|
|
|
|
int CEXEBuild::resolve_call_int(const TCHAR *fn, const TCHAR *str, int fptr, int *ofs)
|
|
{
|
|
if (fptr < 0) return 0;
|
|
int nf=cur_functions->getlen()/sizeof(section);
|
|
section *sec=(section *)cur_functions->get();
|
|
while (nf-- > 0)
|
|
{
|
|
if (sec->name_ptr>0 && sec->name_ptr == fptr)
|
|
{
|
|
ofs[0]=sec->code;
|
|
sec->flags++;
|
|
return 0;
|
|
}
|
|
sec++;
|
|
}
|
|
ERROR_MSG(_T("Error: resolving %s function \"%s\" in %s\n"),str,(TCHAR*)ns_func.get()+fptr,fn);
|
|
ERROR_MSG(_T("Note: uninstall functions must begin with \"un.\", and install functions must not\n"));
|
|
return 1;
|
|
}
|
|
|
|
int CEXEBuild::resolve_instruction(const TCHAR *fn, const TCHAR *str, entry *w, int offs, int start, int end)
|
|
{
|
|
if (w->which == EW_NOP)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[0],offs,start,end)) return 1;
|
|
}
|
|
#ifdef NSIS_SUPPORT_MESSAGEBOX
|
|
else if (w->which == EW_MESSAGEBOX)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[3],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[5],offs,start,end)) return 1;
|
|
}
|
|
#endif
|
|
else if (w->which == EW_IFFILEEXISTS)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[1],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[2],offs,start,end)) return 1;
|
|
}
|
|
else if (w->which == EW_IFFLAG)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[0],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[1],offs,start,end)) return 1;
|
|
}
|
|
#ifdef NSIS_SUPPORT_STROPTS
|
|
else if (w->which == EW_STRCMP)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[2],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[3],offs,start,end)) return 1;
|
|
}
|
|
#endif
|
|
#ifdef NSIS_SUPPORT_INTOPTS
|
|
else if (w->which == EW_INTCMP)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[2],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[3],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[4],offs,start,end)) return 1;
|
|
}
|
|
#endif
|
|
#ifdef NSIS_SUPPORT_HWNDS
|
|
else if (w->which == EW_ISWINDOW)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[1],offs,start,end)) return 1;
|
|
if (resolve_jump_int(fn,&w->offsets[2],offs,start,end)) return 1;
|
|
}
|
|
#endif
|
|
else if (w->which == EW_CALL)
|
|
{
|
|
if (w->offsets[0] >= 0 && w->offsets[1]) // get as jump
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[0],offs,start,end)) return 1;
|
|
}
|
|
else
|
|
{
|
|
if (w->offsets[0] >= 0 && resolve_call_int(fn,str,w->offsets[0],w->offsets)) return 1;
|
|
// if w->offsets[0] >= 0, EW_CALL requires that it 1-based.
|
|
// otherwise, if < 0, it needs an increment anyway (since it
|
|
// was encoded with a -2 base, to prevent it looking like an
|
|
// empty string "")
|
|
w->offsets[0]++;
|
|
}
|
|
}
|
|
#ifdef NSIS_SUPPORT_STROPTS
|
|
else if (w->which == EW_GETFUNCTIONADDR)
|
|
{
|
|
if (w->offsets[1] < 0)
|
|
{
|
|
ERROR_MSG(_T("Error: GetFunctionAddress requires a real function to get address of.\n"));
|
|
return 1;
|
|
}
|
|
|
|
if (resolve_call_int(fn,str,w->offsets[1],&w->offsets[1])) return 1;
|
|
|
|
w->which=EW_ASSIGNVAR;
|
|
w->offsets[1]=add_intstring(w->offsets[1]+1); // +1 here to make 1-based.
|
|
}
|
|
else if (w->which == EW_GETLABELADDR)
|
|
{
|
|
if (resolve_jump_int(fn,&w->offsets[1],offs,start,end)) return 1;
|
|
w->which=EW_ASSIGNVAR;
|
|
w->offsets[1]=add_intstring(w->offsets[1]);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int CEXEBuild::resolve_coderefs(const TCHAR *str)
|
|
{
|
|
// resolve jumps&calls
|
|
{
|
|
section *sec=(section *)cur_functions->get();
|
|
int l=cur_functions->getlen()/sizeof(section);
|
|
entry *w=(entry *)cur_entries->get();
|
|
while (l-- > 0)
|
|
{
|
|
int x;
|
|
for (x = sec->code; x < sec->code+sec->code_size; x ++)
|
|
{
|
|
TCHAR fname[1024];
|
|
wsprintf(fname,_T("function \"%s\""),ns_func.get()+sec->name_ptr);
|
|
if (resolve_instruction(fname,str,w+x,x,sec->code,sec->code+sec->code_size)) return 1;
|
|
}
|
|
sec++;
|
|
}
|
|
|
|
int cnt=0;
|
|
sec=(section *)cur_sections->get();
|
|
l=cur_sections->getlen()/sizeof(section);
|
|
while (l-- > 0)
|
|
{
|
|
int x=sec->name_ptr;
|
|
TCHAR fname[1024];
|
|
const TCHAR *section_name;
|
|
if (x < 0)
|
|
{
|
|
// lang string
|
|
section_name = _T("$(lang string)");
|
|
}
|
|
else
|
|
{
|
|
// normal string
|
|
section_name = cur_strlist->getTchar() + x;
|
|
}
|
|
if (x) wsprintf(fname,_T("%s section \"%s\" (%d)"),str,section_name,cnt);
|
|
else wsprintf(fname,_T("unnamed %s section (%d)"),str,cnt);
|
|
for (x = sec->code; x < sec->code+sec->code_size; x ++)
|
|
{
|
|
if (resolve_instruction(fname,str,w+x,x,sec->code,sec->code+sec->code_size))
|
|
return 1;
|
|
}
|
|
sec++;
|
|
cnt++;
|
|
}
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
#ifdef NSIS_SUPPORT_CODECALLBACKS
|
|
if (cur_pages->getlen()) {
|
|
page *p=(page *)cur_pages->get();
|
|
int i = 0;
|
|
while (i < cur_header->blocks[NB_PAGES].num) {
|
|
TCHAR pagestr[1024];
|
|
wsprintf(pagestr, _T("%s pages"), str);
|
|
if (resolve_call_int(pagestr,p->dlg_id?_T("pre-page"):_T("create-page"),p->prefunc,&p->prefunc)) return 1;
|
|
if (resolve_call_int(pagestr,_T("show-page"),p->showfunc,&p->showfunc)) return 1;
|
|
if (resolve_call_int(pagestr,_T("leave-page"),p->leavefunc,&p->leavefunc)) return 1;
|
|
p++;
|
|
i++;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
#ifdef NSIS_SUPPORT_CODECALLBACKS
|
|
// resolve callbacks
|
|
{
|
|
struct {
|
|
const TCHAR *name;
|
|
int *p;
|
|
} callbacks[] = {
|
|
{_T("%s.onInit"), &cur_header->code_onInit},
|
|
{_T("%s.on%sInstSuccess"), &cur_header->code_onInstSuccess},
|
|
{_T("%s.on%sInstFailed"), &cur_header->code_onInstFailed},
|
|
{_T("%s.onUserAbort"), &cur_header->code_onUserAbort},
|
|
{_T("%s.onVerifyInstDir"), &cur_header->code_onVerifyInstDir},
|
|
#ifdef NSIS_CONFIG_ENHANCEDUI_SUPPORT
|
|
{_T("%s.onGUIInit"), &cur_header->code_onGUIInit},
|
|
{_T("%s.onGUIEnd"), &cur_header->code_onGUIEnd},
|
|
{_T("%s.onMouseOverSection"), &cur_header->code_onMouseOverSection},
|
|
#endif//NSIS_CONFIG_ENHANCEDUI_SUPPORT
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
{_T("%s.onSelChange"), &cur_header->code_onSelChange},
|
|
#endif//NSIS_CONFIG_COMPONENTPAGE
|
|
#ifdef NSIS_SUPPORT_REBOOT
|
|
{_T("%s.onRebootFailed"), &cur_header->code_onRebootFailed},
|
|
#endif//NSIS_SUPPORT_REBOOT
|
|
{0, 0}
|
|
};
|
|
|
|
for (int i = 0; callbacks[i].name; i++) {
|
|
const TCHAR *un = uninstall_mode ? _T("un") : _T("");
|
|
TCHAR fname[1024];
|
|
wsprintf(fname, callbacks[i].name, un, un);
|
|
TCHAR cbstr[1024];
|
|
wsprintf(cbstr, _T("%s callback"), str);
|
|
TCHAR cbstr2[1024];
|
|
wsprintf(cbstr2, _T("%s.callbacks"), un);
|
|
|
|
if (resolve_call_int(cbstr,cbstr2,ns_func.find(fname,0),callbacks[i].p))
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
#endif//NSIS_SUPPORT_CODECALLBACKS
|
|
|
|
// optimize unused functions
|
|
{
|
|
section *sec=(section *)cur_functions->get();
|
|
int l=cur_functions->getlen()/sizeof(section);
|
|
entry *w=(entry*)cur_entries->get();
|
|
while (l-- > 0)
|
|
{
|
|
if (sec->name_ptr)
|
|
{
|
|
if (!sec->flags)
|
|
{
|
|
if (sec->code_size>0)
|
|
{
|
|
warning(_T("%s function \"%s\" not referenced - zeroing code (%d-%d) out\n"),str,
|
|
ns_func.get()+sec->name_ptr,
|
|
sec->code,sec->code+sec->code_size);
|
|
memset(w+sec->code,0,sec->code_size*sizeof(entry));
|
|
}
|
|
}
|
|
}
|
|
sec++;
|
|
}
|
|
}
|
|
|
|
// give warnings on unused labels
|
|
{
|
|
section *t=(section*)cur_labels->get();
|
|
int n=cur_labels->getlen()/sizeof(section);
|
|
while (n-->0)
|
|
{
|
|
if (!t->flags)
|
|
{
|
|
TCHAR *n=(TCHAR*)ns_label.get()+t->name_ptr;
|
|
if (*n == _T('.')) warning(_T("global label \"%s\" not used"),n);
|
|
else warning(_T("label \"%s\" not used"),n);
|
|
}
|
|
t++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
int CEXEBuild::add_page(int type)
|
|
{
|
|
page pg = {
|
|
0,
|
|
0,
|
|
#ifdef NSIS_SUPPORT_CODECALLBACKS
|
|
-1,
|
|
-1,
|
|
-1,
|
|
#endif
|
|
0,
|
|
};
|
|
|
|
#ifndef NSIS_CONFIG_LICENSEPAGE
|
|
if (type == PAGE_LICENSE)
|
|
{
|
|
ERROR_MSG(_T("Error: can't add license page, NSIS_CONFIG_LICENSEPAGE not defined.\n"));
|
|
return PS_ERROR;
|
|
}
|
|
#endif
|
|
#ifndef NSIS_CONFIG_COMPONENTPAGE
|
|
if (type == PAGE_COMPONENTS)
|
|
{
|
|
ERROR_MSG(_T("Error: can't add components page, NSIS_CONFIG_COMPONENTPAGE not defined.\n"));
|
|
return PS_ERROR;
|
|
}
|
|
#endif
|
|
#ifndef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (type == PAGE_COMPONENTS)
|
|
{
|
|
ERROR_MSG(_T("Error: can't add uninstConfirm page, NSIS_CONFIG_UNINSTALL_SUPPORT not defined.\n"));
|
|
return PS_ERROR;
|
|
}
|
|
#endif
|
|
|
|
struct {
|
|
int wndproc_id;
|
|
int dlg_id;
|
|
const TCHAR *name;
|
|
} ids[] = {
|
|
{PWP_CUSTOM, 0, _T("custom")}, // custom
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
{PWP_LICENSE, IDD_LICENSE, _T("license")}, // license
|
|
#else
|
|
{0, IDD_LICENSE, _T("license")}, // license
|
|
#endif
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
{PWP_SELCOM, IDD_SELCOM, _T("components")}, // components
|
|
#else
|
|
{0, IDD_SELCOM, _T("components")}, // components
|
|
#endif
|
|
{PWP_DIR, IDD_DIR, _T("directory")}, // directory
|
|
{PWP_INSTFILES, IDD_INSTFILES, _T("instfiles")}, // instfiles
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
{PWP_UNINST, IDD_UNINST, _T("uninstConfirm")}, // uninstConfirm
|
|
#else
|
|
{0, IDD_UNINST, _T("uninstConfirm")}, // uninstConfirm
|
|
#endif
|
|
{PWP_COMPLETED, -1, NULL} // completed
|
|
};
|
|
|
|
pg.wndproc_id = ids[type].wndproc_id;
|
|
pg.dlg_id = ids[type].dlg_id;
|
|
|
|
cur_pages->add(&pg,sizeof(page));
|
|
|
|
cur_page = (page *)cur_pages->get() + cur_header->blocks[NB_PAGES].num++;
|
|
|
|
cur_page_type = type;
|
|
|
|
set_code_type_predefines(ids[type].name);
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::page_end()
|
|
{
|
|
cur_page = 0;
|
|
|
|
set_code_type_predefines();
|
|
return PS_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifdef NSIS_SUPPORT_VERSION_INFO
|
|
int CEXEBuild::AddVersionInfo()
|
|
{
|
|
GrowBuf VerInfoStream;
|
|
|
|
// Should probably check for (4 & version_fixedflags) here, but VIProductVersion without VIAddVersionKey
|
|
// fails silently, so VIFileVersion does the same...
|
|
if ( rVersionInfo.GetStringTablesCount() > 0 )
|
|
{
|
|
if ( !(1 & version_fixedflags) )
|
|
{
|
|
ERROR_MSG(_T("Error: VIProductVersion is required when other version information functions are used.\n"));
|
|
return PS_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if ( !(2 & version_fixedflags) )
|
|
{
|
|
// This error string should match the one used by the TOK_VI_SETFILEVERSION handler
|
|
ERROR_MSG(_T("Error: invalid %s format, should be X.X.X.X\n"),_T("VIProductVersion"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
try
|
|
{
|
|
init_res_editor();
|
|
for ( int i = 0; i < rVersionInfo.GetStringTablesCount(); i++ )
|
|
{
|
|
LANGID lang_id = rVersionInfo.GetLangID(i);
|
|
int code_page = rVersionInfo.GetCodePage(i);
|
|
|
|
const TCHAR *lang_name = GetLangNameAndCPForVersionResource(lang_id, NULL, false);
|
|
|
|
const TCHAR *recverkeys =
|
|
_T("FileVersion\0")
|
|
_T("FileDescription\0")
|
|
_T("LegalCopyright\0");
|
|
for(;;)
|
|
{
|
|
if ( !*recverkeys ) break;
|
|
if ( !rVersionInfo.FindKey(lang_id, code_page, recverkeys) )
|
|
warning(_T("Generating version information for language \"%04d-%s\" without standard key \"%s\""), lang_id, lang_name, recverkeys);
|
|
recverkeys += _tcsclen(recverkeys) + 1;
|
|
}
|
|
|
|
rVersionInfo.ExportToStream(VerInfoStream, i);
|
|
res_editor->UpdateResource(RT_VERSION, 1, lang_id, (BYTE*)VerInfoStream.get(), VerInfoStream.getlen());
|
|
}
|
|
}
|
|
catch (exception& err) {
|
|
ERROR_MSG(_T("Error adding version information: %s\n"), CtoTStrParam(err.what()));
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
#endif // NSIS_SUPPORT_VERSION_INFO
|
|
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
|
|
int CEXEBuild::ProcessPages()
|
|
{
|
|
SCRIPT_MSG(_T("Processing pages... "));
|
|
|
|
int license_normal=0;
|
|
int license_fsrb=0;
|
|
int license_fscb=0;
|
|
int selcom=0;
|
|
int dir=0;
|
|
int uninstconfirm=0;
|
|
int instlog=0, instlog_used;
|
|
int main=0;
|
|
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
again:
|
|
#endif
|
|
|
|
instlog_used = 0;
|
|
|
|
#ifdef NSIS_CONFIG_SILENT_SUPPORT
|
|
if ((cur_header->flags & (CH_FLAGS_SILENT|CH_FLAGS_SILENT_LOG)) == 0)
|
|
#endif
|
|
{
|
|
main++;
|
|
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
#define LS(inst, uninst) (uninstall_mode ? uninst : inst)
|
|
#else
|
|
#define LS(inst, uninst) inst
|
|
#endif
|
|
|
|
DefineInnerLangString(NLF_BRANDING);
|
|
|
|
if (!cur_pages->getlen()) {
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (uninstall_mode) {
|
|
if (HasUserDefined(NLF_UNINST_TEXT)) {
|
|
add_page(PAGE_UNINSTCONFIRM);
|
|
page_end();
|
|
}
|
|
add_page(PAGE_INSTFILES);
|
|
page_end();
|
|
add_page(PAGE_COMPLETED);
|
|
page_end();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
if (HasUserDefined(NLF_LICENSE_TEXT) && HasUserDefined(NLF_LICENSE_DATA)) {
|
|
add_page(PAGE_LICENSE);
|
|
page_end();
|
|
}
|
|
#endif
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
if (HasUserDefined(NLF_COMP_TEXT)) {
|
|
add_page(PAGE_COMPONENTS);
|
|
page_end();
|
|
}
|
|
#endif
|
|
if (HasUserDefined(NLF_DIR_TEXT)) {
|
|
add_page(PAGE_DIRECTORY);
|
|
page_end();
|
|
}
|
|
add_page(PAGE_INSTFILES);
|
|
page_end();
|
|
add_page(PAGE_COMPLETED);
|
|
page_end();
|
|
}
|
|
}
|
|
// start processing the pages
|
|
{
|
|
int i = 0;
|
|
page *p = (page *) cur_pages->get();
|
|
|
|
for (i = 0; i < cur_header->blocks[NB_PAGES].num; i++, p++) {
|
|
page *pp = 0;
|
|
|
|
if (i) {
|
|
pp = p - 1;
|
|
|
|
// set back button
|
|
p->flags |= PF_BACK_SHOW;
|
|
if (pp->wndproc_id != PWP_COMPLETED && p->wndproc_id != PWP_COMPLETED && p->wndproc_id != PWP_INSTFILES)
|
|
p->flags |= PF_BACK_ENABLE;
|
|
if (!p->back)
|
|
p->back = DefineInnerLangString(NLF_BTN_BACK);
|
|
|
|
// set previous page's next button
|
|
if (!pp->next) {
|
|
int str = 0;
|
|
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
if (pp->wndproc_id == PWP_LICENSE && (!(pp->flags & PF_LICENSE_FORCE_SELECTION) || HasUserDefined(NLF_BTN_LICENSE)))
|
|
str = NLF_BTN_LICENSE;
|
|
else
|
|
#endif
|
|
if (p->wndproc_id == PWP_INSTFILES)
|
|
str = LS(NLF_BTN_INSTALL, NLF_BTN_UNINSTALL);
|
|
else
|
|
str = NLF_BTN_NEXT;
|
|
|
|
pp->next = DefineInnerLangString(str);
|
|
}
|
|
|
|
// set previous page's click next text
|
|
if (!pp->clicknext) {
|
|
int str = 0;
|
|
|
|
if (p->wndproc_id == PWP_INSTFILES)
|
|
str = LS(NLF_CLICK_INSTALL, NLF_CLICK_UNINSTALL);
|
|
else
|
|
str = NLF_CLICK_NEXT;
|
|
|
|
pp->clicknext = DefineInnerLangString(str);
|
|
}
|
|
}
|
|
|
|
// enable next button
|
|
if (p->wndproc_id != PWP_INSTFILES)
|
|
p->flags |= PF_NEXT_ENABLE;
|
|
|
|
// set cancel button
|
|
if (!p->cancel)
|
|
p->cancel = DefineInnerLangString(NLF_BTN_CANCEL);
|
|
if (p->wndproc_id != PWP_INSTFILES && p->wndproc_id != PWP_COMPLETED)
|
|
p->flags |= PF_CANCEL_ENABLE;
|
|
|
|
// set caption
|
|
struct {
|
|
int caption;
|
|
int ucaption;
|
|
} captions[] = {
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
{NLF_SUBCAPTION_LICENSE, NLF_SUBCAPTION_LICENSE},
|
|
#endif
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
{NLF_SUBCAPTION_OPTIONS, NLF_SUBCAPTION_OPTIONS},
|
|
#endif
|
|
{NLF_SUBCAPTION_DIR, NLF_SUBCAPTION_DIR},
|
|
{NLF_SUBCAPTION_INSTFILES, NLF_USUBCAPTION_INSTFILES},
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
{NLF_USUBCAPTION_CONFIRM, NLF_USUBCAPTION_CONFIRM},
|
|
#endif
|
|
{NLF_SUBCAPTION_COMPLETED, NLF_USUBCAPTION_COMPLETED}
|
|
};
|
|
|
|
if (!p->caption && p->wndproc_id != PWP_CUSTOM) {
|
|
p->caption = DefineInnerLangString(LS(captions[p->wndproc_id].caption, captions[p->wndproc_id].ucaption));
|
|
}
|
|
|
|
// set texts
|
|
switch (p->dlg_id) {
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
case IDD_LICENSE:
|
|
case IDD_LICENSE_FSRB:
|
|
case IDD_LICENSE_FSCB:
|
|
{
|
|
if (!(p->flags & PF_PAGE_EX))
|
|
p->dlg_id = license_res_id;
|
|
if (!(p->flags & (PF_LICENSE_FORCE_SELECTION | PF_LICENSE_NO_FORCE_SELECTION)))
|
|
p->dlg_id = license_res_id;
|
|
|
|
p->flags |= PF_NO_NEXT_FOCUS;
|
|
|
|
if (!p->parms[1])
|
|
p->parms[1] = DefineInnerLangString(NLF_LICENSE_DATA, 0);
|
|
|
|
if (p->dlg_id == IDD_LICENSE) {
|
|
if (!p->parms[0])
|
|
p->parms[0] = DefineInnerLangString(LS(NLF_LICENSE_TEXT, NLF_ULICENSE_TEXT));
|
|
|
|
license_normal++;
|
|
}
|
|
else if (p->dlg_id == IDD_LICENSE_FSCB) {
|
|
p->flags |= PF_LICENSE_FORCE_SELECTION;
|
|
|
|
if (!p->parms[0])
|
|
p->parms[0] = DefineInnerLangString(LS(NLF_LICENSE_TEXT_FSCB, NLF_ULICENSE_TEXT_FSCB));
|
|
if (!p->parms[2])
|
|
p->parms[2] = DefineInnerLangString(NLF_BTN_LICENSE_AGREE);
|
|
|
|
license_fscb++;
|
|
}
|
|
else if (p->dlg_id == IDD_LICENSE_FSRB) {
|
|
p->flags |= PF_LICENSE_FORCE_SELECTION;
|
|
|
|
if (!p->parms[0])
|
|
p->parms[0] = DefineInnerLangString(LS(NLF_LICENSE_TEXT_FSRB, NLF_ULICENSE_TEXT_FSRB));
|
|
if (!p->parms[2])
|
|
p->parms[2] = DefineInnerLangString(NLF_BTN_LICENSE_AGREE);
|
|
if (!p->parms[3])
|
|
p->parms[3] = DefineInnerLangString(NLF_BTN_LICENSE_DISAGREE);
|
|
|
|
license_fsrb++;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
case IDD_SELCOM:
|
|
{
|
|
if (!p->parms[0])
|
|
p->parms[0] = DefineInnerLangString(LS(NLF_COMP_TEXT, NLF_UCOMP_TEXT));
|
|
if (!p->parms[1])
|
|
p->parms[1] = DefineInnerLangString(LS(NLF_COMP_SUBTEXT1, NLF_UCOMP_SUBTEXT1));
|
|
if (!p->parms[2])
|
|
p->parms[2] = DefineInnerLangString(LS(NLF_COMP_SUBTEXT2, NLF_UCOMP_SUBTEXT2));
|
|
if (!p->parms[3] && !uninstall_mode && HasUserDefined(NLF_COMP_SUBTEXT1))
|
|
p->parms[3] = p->parms[1];
|
|
if (!p->parms[4] && !uninstall_mode && HasUserDefined(NLF_COMP_SUBTEXT2))
|
|
p->parms[4] = p->parms[2];
|
|
else if (!p->parms[4])
|
|
p->parms[4] = DefineInnerLangString(LS(NLF_COMP_SUBTEXT1_NO_INST_TYPES, NLF_UCOMP_SUBTEXT1_NO_INST_TYPES));
|
|
|
|
DefineInnerLangString(NLF_SPACE_REQ);
|
|
DefineInnerLangString(NLF_BYTE);
|
|
DefineInnerLangString(NLF_KILO);
|
|
DefineInnerLangString(NLF_MEGA);
|
|
DefineInnerLangString(NLF_GIGA);
|
|
|
|
selcom++;
|
|
break;
|
|
}
|
|
#endif
|
|
case IDD_DIR:
|
|
{
|
|
if (!p->parms[0])
|
|
p->parms[0] = DefineInnerLangString(LS(NLF_DIR_TEXT, NLF_UDIR_TEXT));
|
|
if (!p->parms[1])
|
|
p->parms[1] = DefineInnerLangString(LS(NLF_DIR_SUBTEXT, NLF_UDIR_SUBTEXT));
|
|
if (!p->parms[2])
|
|
p->parms[2] = DefineInnerLangString(NLF_BTN_BROWSE);
|
|
if (!p->parms[3])
|
|
p->parms[3] = DefineInnerLangString(LS(NLF_DIR_BROWSETEXT, NLF_UDIR_BROWSETEXT));
|
|
if (!p->parms[4])
|
|
p->parms[4] = m_UserVarNames.get(_T("INSTDIR"));
|
|
else
|
|
p->parms[4]--;
|
|
|
|
DefineInnerLangString(NLF_SPACE_AVAIL);
|
|
DefineInnerLangString(NLF_SPACE_REQ);
|
|
DefineInnerLangString(NLF_BYTE);
|
|
DefineInnerLangString(NLF_KILO);
|
|
DefineInnerLangString(NLF_MEGA);
|
|
DefineInnerLangString(NLF_GIGA);
|
|
#ifdef NSIS_CONFIG_LOG
|
|
DefineInnerLangString(NLF_LOG_INSTALL_PROCESS);
|
|
#endif
|
|
|
|
dir++;
|
|
break;
|
|
}
|
|
case IDD_INSTFILES:
|
|
{
|
|
if (!p->parms[1])
|
|
p->parms[1] = DefineInnerLangString(NLF_BTN_DETAILS);
|
|
if (!p->parms[2])
|
|
p->parms[2] = DefineInnerLangString(NLF_COMPLETED);
|
|
|
|
DefineInnerLangString(NLF_COPY_DETAILS);
|
|
|
|
instlog++;
|
|
instlog_used++;
|
|
break;
|
|
}
|
|
case IDD_UNINST:
|
|
{
|
|
if (!p->parms[0])
|
|
p->parms[0] = DefineInnerLangString(NLF_UNINST_TEXT);
|
|
if (!p->parms[1])
|
|
p->parms[1] = DefineInnerLangString(NLF_UNINST_SUBTEXT);
|
|
if (!p->parms[4])
|
|
p->parms[4] = m_UserVarNames.get(_T("INSTDIR"));
|
|
else
|
|
p->parms[4]--;
|
|
|
|
uninstconfirm++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
p->flags &= ~PF_PAGE_EX;
|
|
}
|
|
|
|
p--;
|
|
|
|
if (!p->next)
|
|
p->next = DefineInnerLangString(NLF_BTN_CLOSE);
|
|
if (p->wndproc_id == PWP_COMPLETED)
|
|
(p-1)->next = DefineInnerLangString(NLF_BTN_CLOSE);
|
|
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (uninstall_mode) {
|
|
if (!uenable_last_page_cancel && instlog_used)
|
|
p->flags &= ~PF_CANCEL_ENABLE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!enable_last_page_cancel && instlog_used)
|
|
p->flags &= ~PF_CANCEL_ENABLE;
|
|
}
|
|
|
|
if (!instlog_used) {
|
|
warning(_T("%sage instfiles not used, no sections will be executed!"), uninstall_mode ? _T("Uninstall p") : _T("P"));
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (!uninstall_mode) {
|
|
set_uninstall_mode(1);
|
|
goto again;
|
|
}
|
|
else
|
|
set_uninstall_mode(0);
|
|
#endif//NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
|
|
|
|
SCRIPT_MSG(_T("Done!\n"));
|
|
|
|
#define REMOVE_ICON(id) if (disable_window_icon) { \
|
|
BYTE* dlg = res_editor->GetResource(RT_DIALOG, id, NSIS_DEFAULT_LANG); \
|
|
if (dlg) { \
|
|
CDialogTemplate dt(dlg,build_unicode,uDefCodePage); \
|
|
res_editor->FreeResource(dlg); \
|
|
if (dt.RemoveItem(IDC_ULICON)) { \
|
|
DialogItemTemplate* text = dt.GetItem(IDC_INTROTEXT); \
|
|
DialogItemTemplate* prog = dt.GetItem(IDC_PROGRESS); \
|
|
if (text) { \
|
|
text->sWidth += text->sX; \
|
|
text->sX = 0; \
|
|
} \
|
|
if (prog) { \
|
|
prog->sWidth += prog->sX; \
|
|
prog->sX = 0; \
|
|
} \
|
|
\
|
|
DWORD dwSize; \
|
|
dlg = dt.Save(dwSize); \
|
|
res_editor->UpdateResource(RT_DIALOG, id, NSIS_DEFAULT_LANG, dlg, dwSize); \
|
|
delete [] dlg; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
try {
|
|
SCRIPT_MSG(_T("Removing unused resources... "));
|
|
init_res_editor();
|
|
#ifdef NSIS_CONFIG_LICENSEPAGE
|
|
if (!license_normal) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_LICENSE, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_LICENSE);
|
|
if (!license_fsrb) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_LICENSE_FSRB, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_LICENSE_FSRB);
|
|
if (!license_fscb) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_LICENSE_FSCB, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_LICENSE_FSCB);
|
|
#endif // NSIS_CONFIG_LICENSEPAGE
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
if (!selcom) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_SELCOM, NSIS_DEFAULT_LANG, 0, 0);
|
|
res_editor->UpdateResource(RT_BITMAP, IDB_BITMAP1, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_SELCOM);
|
|
#endif // NSIS_CONFIG_COMPONENTPAGE
|
|
if (!dir) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_DIR, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_DIR);
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (!uninstconfirm) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_UNINST, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_UNINST);
|
|
#endif // NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (!instlog) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_INSTFILES, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
else REMOVE_ICON(IDD_INSTFILES);
|
|
if (!main) {
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_INST, NSIS_DEFAULT_LANG, 0, 0);
|
|
if (!build_compress_whole && !build_crcchk)
|
|
res_editor->UpdateResource(RT_DIALOG, IDD_VERIFY, NSIS_DEFAULT_LANG, 0, 0);
|
|
}
|
|
|
|
SCRIPT_MSG(_T("Done!\n"));
|
|
}
|
|
catch (exception& err) {
|
|
ERROR_MSG(_T("\nError: %s\n"), CtoTStrParam(err.what()));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
#endif // NSIS_CONFIG_VISIBLE_SUPPORT
|
|
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
void CEXEBuild::PrepareInstTypes()
|
|
{
|
|
if (!(cur_header->flags & CH_FLAGS_NO_CUSTOM))
|
|
cur_header->install_types[NSIS_MAX_INST_TYPES] = DefineInnerLangString(NLF_COMP_CUSTOM);
|
|
|
|
// set insttype list for RO sections that didn't use SectionIn
|
|
int i = cur_header->blocks[NB_SECTIONS].num;
|
|
section *sections = (section *) cur_sections->get();
|
|
|
|
while (i--)
|
|
{
|
|
if (sections[i].flags & SF_RO && !sections[i].install_types)
|
|
sections[i].install_types = ~0;
|
|
}
|
|
|
|
// set selection to first insttype
|
|
if (cur_header->install_types[0])
|
|
{
|
|
int i = cur_header->blocks[NB_SECTIONS].num;
|
|
section *sections = (section *) cur_sections->get();
|
|
|
|
// if /o was used abort since the user did his manual choice
|
|
while (i--)
|
|
if ((sections[i].flags & SF_SELECTED) == 0)
|
|
return;
|
|
|
|
i = cur_header->blocks[NB_SECTIONS].num;
|
|
|
|
while (i--)
|
|
if ((sections[i].install_types & 1) == 0)
|
|
sections[i].flags &= ~SF_SELECTED;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CEXEBuild::AddStandardStrings()
|
|
{
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (uninstall_mode)
|
|
{
|
|
cur_header->str_uninstchild = add_string(_T("$TEMP\\$1u_.exe"));
|
|
cur_header->str_uninstcmd = add_string(_T("\"$TEMP\\$1u_.exe\" $0 _?=$INSTDIR\\"));
|
|
}
|
|
#endif//NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
#ifdef NSIS_SUPPORT_MOVEONREBOOT
|
|
cur_header->str_wininit = add_string(_T("$WINDIR\\wininit.ini"));
|
|
#endif//NSIS_SUPPORT_MOVEONREBOOT
|
|
}
|
|
|
|
void CEXEBuild::PrepareHeaders(IGrowBuf *hdrbuf)
|
|
{
|
|
GrowBuf blocks_buf;
|
|
growbuf_writer_sink sink(&blocks_buf, build_unicode);
|
|
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
cur_header->blocks[NB_PAGES].offset = sizeof(header) + blocks_buf.getlen();
|
|
page_writer::write_block(cur_pages, &sink);
|
|
#endif
|
|
|
|
cur_header->blocks[NB_SECTIONS].offset = sizeof(header) + blocks_buf.getlen();
|
|
section_writer::write_block(cur_sections, &sink);
|
|
|
|
cur_header->blocks[NB_ENTRIES].offset = sizeof(header) + blocks_buf.getlen();
|
|
entry_writer::write_block(cur_entries, &sink);
|
|
|
|
cur_header->blocks[NB_STRINGS].offset = sizeof(header) + blocks_buf.getlen();
|
|
#ifdef _UNICODE
|
|
if (!build_unicode)
|
|
blocks_buf.add(cur_strlist->getAnsi(), cur_strlist->getcount());
|
|
else
|
|
#endif
|
|
blocks_buf.add(cur_strlist->getTchar(), cur_strlist->getcount()*sizeof(TCHAR));
|
|
|
|
cur_header->blocks[NB_LANGTABLES].offset = sizeof(header) + blocks_buf.getlen();
|
|
lang_table_writer::write_block(cur_langtables, &sink, cur_header->langtable_size);
|
|
|
|
cur_header->blocks[NB_CTLCOLORS].offset = sizeof(header) + blocks_buf.getlen();
|
|
ctlcolors_writer::write_block(cur_ctlcolors, &sink);
|
|
|
|
#ifdef NSIS_SUPPORT_BGBG
|
|
if (cur_header->bg_color1 != -1)
|
|
{
|
|
bg_font.lfFaceName[LF_FACESIZE-1] = 0;
|
|
|
|
cur_header->blocks[NB_BGFONT].offset = sizeof(header) + blocks_buf.getlen();
|
|
|
|
LOGFONT_writer w(&sink);
|
|
w.write(&bg_font);
|
|
}
|
|
#endif
|
|
|
|
growbuf_writer_sink sink2(hdrbuf, build_unicode);
|
|
header_writer header(&sink2);
|
|
header.write(cur_header);
|
|
|
|
sink2.write_growbuf(&blocks_buf);
|
|
}
|
|
|
|
int CEXEBuild::SetVarsSection()
|
|
{
|
|
try {
|
|
init_res_editor();
|
|
|
|
VerifyDeclaredUserVarRefs(&m_UserVarNames);
|
|
int MaxUserVars = m_UserVarNames.getnum();
|
|
int stringSize = NSIS_MAX_STRLEN*(build_unicode?sizeof(TCHAR):sizeof(char));
|
|
if (!res_editor->SetPESectionVirtualSize(NSIS_VARS_SECTION, MaxUserVars * stringSize))
|
|
{
|
|
ERROR_MSG(_T("Internal compiler error #12346: invalid exehead cannot find section \"%s\"!\n"), _T(NSIS_VARS_SECTION));
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
catch (exception& err) {
|
|
ERROR_MSG(_T("\nError: %s\n"), CtoTStrParam(err.what()));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::SetManifest()
|
|
{
|
|
try {
|
|
init_res_editor();
|
|
// This should stay ANSI
|
|
string manifest = manifest::generate(manifest_comctl, manifest_exec_level, manifest_dpiaware, manifest_sosl);
|
|
|
|
if (manifest == "")
|
|
return PS_OK;
|
|
|
|
// Saved directly as binary into the exe.
|
|
res_editor->UpdateResource(MAKEINTRESOURCE(24), 1, NSIS_DEFAULT_LANG, (LPBYTE) manifest.c_str(), manifest.length());
|
|
}
|
|
catch (exception& err) {
|
|
ERROR_MSG(_T("Error setting manifest: %s\n"), CtoTStrParam(err.what()));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::UpdatePEHeader()
|
|
{
|
|
try {
|
|
PIMAGE_NT_HEADERS headers = CResourceEditor::GetNTHeaders(m_exehead);
|
|
// workaround for bug #2697027, #2725883, #2803097
|
|
headers->OptionalHeader.MajorImageVersion = 6;
|
|
headers->OptionalHeader.MinorImageVersion = 0;
|
|
// terminal services aware
|
|
headers->OptionalHeader.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE;
|
|
} catch (std::runtime_error& err) {
|
|
ERROR_MSG(_T("Error updating PE headers: %s\n"), CtoTStrParam(err.what()));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
void CEXEBuild::set_default_output_filename(const tstring& filename)
|
|
{
|
|
if (build_output_filename[0] == 0)
|
|
_tcsnccpy(build_output_filename,filename.c_str(),1024-1);
|
|
}
|
|
|
|
int CEXEBuild::check_write_output_errors() const
|
|
{
|
|
if (has_called_write_output)
|
|
{
|
|
ERROR_MSG(_T("Error (write_output): write_output already called, can't continue\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (!build_output_filename[0])
|
|
{
|
|
ERROR_MSG(_T("Error: invalid script: never had OutFile command\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (!build_sections.getlen())
|
|
{
|
|
ERROR_MSG(_T("Error: invalid script: no sections specified\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (!build_entries.getlen())
|
|
{
|
|
ERROR_MSG(_T("Error: invalid script: no entries specified\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (build_cursection)
|
|
{
|
|
ERROR_MSG(_T("Error: Section left open at EOF\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (sectiongroup_open_cnt)
|
|
{
|
|
ERROR_MSG(_T("Error: SectionGroup left open at EOF\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (cur_page)
|
|
{
|
|
ERROR_MSG(_T("Error: PageEx left open at EOF\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
// deal with functions, for both install and uninstall modes.
|
|
if (build_cursection_isfunc)
|
|
{
|
|
ERROR_MSG(_T("Error: Function left open at EOF\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::prepare_uninstaller() {
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (ubuild_entries.getlen())
|
|
{
|
|
if (!uninstaller_writes_used)
|
|
{
|
|
warning(_T("Uninstaller script code found but WriteUninstaller never used - no uninstaller will be created."));
|
|
return PS_OK;
|
|
}
|
|
|
|
build_uninst.flags|=build_header.flags&(CH_FLAGS_PROGRESS_COLORED|CH_FLAGS_NO_ROOT_DIR);
|
|
|
|
set_uninstall_mode(1);
|
|
|
|
DefineInnerLangString(NLF_UCAPTION);
|
|
|
|
if (resolve_coderefs(_T("uninstall")))
|
|
return PS_ERROR;
|
|
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
// set sections to the first insttype
|
|
PrepareInstTypes();
|
|
#endif
|
|
|
|
// Add standard strings to string table
|
|
AddStandardStrings();
|
|
|
|
set_uninstall_mode(0);
|
|
}
|
|
else if (uninstaller_writes_used)
|
|
{
|
|
ERROR_MSG(_T("Error: no Uninstall section specified, but WriteUninstaller used %d time(s)\n"),uninstaller_writes_used);
|
|
return PS_ERROR;
|
|
}
|
|
#endif//NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::pack_exe_header()
|
|
{
|
|
if (!(build_packname[0] && build_packcmd[0])) {
|
|
// header not asked to be packed
|
|
return PS_OK;
|
|
}
|
|
|
|
// write out exe header, pack, read back in, and
|
|
// update the header info
|
|
FILE *tmpfile=FOPEN(build_packname,("wb"));
|
|
if (!tmpfile)
|
|
{
|
|
ERROR_MSG(_T("Error: writing temporary file \"%s\" for pack\n"),build_packname);
|
|
return PS_ERROR;
|
|
}
|
|
fwrite(m_exehead,1,m_exehead_size,tmpfile);
|
|
fclose(tmpfile);
|
|
if (sane_system(build_packcmd) == -1)
|
|
{
|
|
_tremove(build_packname);
|
|
ERROR_MSG(_T("Error: calling packer on \"%s\"\n"),build_packname);
|
|
return PS_ERROR;
|
|
}
|
|
|
|
int result = update_exehead(build_packname);
|
|
_tremove(build_packname);
|
|
|
|
if (result != PS_OK)
|
|
{
|
|
ERROR_MSG(_T("Error: reading temporary file \"%s\" after pack\n"),build_packname);
|
|
return result;
|
|
}
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::write_output(void)
|
|
{
|
|
#ifndef NSIS_CONFIG_CRC_SUPPORT
|
|
build_crcchk=0;
|
|
#endif
|
|
|
|
RET_UNLESS_OK( check_write_output_errors() );
|
|
|
|
has_called_write_output=true;
|
|
|
|
#ifdef NSIS_CONFIG_PLUGIN_SUPPORT
|
|
RET_UNLESS_OK( add_plugins_dir_initializer() );
|
|
#endif //NSIS_CONFIG_PLUGIN_SUPPORT
|
|
|
|
#ifdef NSIS_SUPPORT_VERSION_INFO
|
|
RET_UNLESS_OK( AddVersionInfo() );
|
|
#endif //NSIS_SUPPORT_VERSION_INFO
|
|
|
|
RET_UNLESS_OK( prepare_uninstaller() );
|
|
|
|
DefineInnerLangString(NLF_CAPTION);
|
|
if (resolve_coderefs(_T("install")))
|
|
return PS_ERROR;
|
|
|
|
#ifdef NSIS_CONFIG_COMPONENTPAGE
|
|
// set sections to the first insttype
|
|
PrepareInstTypes();
|
|
#endif
|
|
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
RET_UNLESS_OK( ProcessPages() );
|
|
#endif //NSIS_CONFIG_VISIBLE_SUPPORT
|
|
|
|
// Generate language tables
|
|
RET_UNLESS_OK( GenerateLangTables() );
|
|
|
|
// Setup user variables PE section
|
|
RET_UNLESS_OK( SetVarsSection() );
|
|
|
|
// Set XML manifest
|
|
RET_UNLESS_OK( SetManifest() );
|
|
|
|
// Add standard strings to string table
|
|
AddStandardStrings();
|
|
|
|
try {
|
|
// Load icon from exe, if needed
|
|
if (installer_icon.empty())
|
|
{
|
|
init_res_editor();
|
|
installer_icon = load_icon_res(res_editor, IDI_ICON2);
|
|
}
|
|
|
|
// Set icon
|
|
set_icon(res_editor, IDI_ICON2, installer_icon, uninstaller_icon);
|
|
|
|
// Save all changes to the exe header
|
|
close_res_editor();
|
|
}
|
|
catch (exception& err) {
|
|
ERROR_MSG(_T("\nError: %s\n"), CtoTStrParam(err.what()));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
// Final PE touch-ups
|
|
RET_UNLESS_OK( UpdatePEHeader() );
|
|
|
|
RET_UNLESS_OK( pack_exe_header() );
|
|
|
|
|
|
build_optimize_datablock=0;
|
|
|
|
int data_block_size_before_uninst = build_datablock.getlen();
|
|
|
|
RET_UNLESS_OK( uninstall_generate() );
|
|
|
|
crc32_t crc=0;
|
|
|
|
{
|
|
tstring full_path = get_full_path(build_output_filename);
|
|
notify(MAKENSIS_NOTIFY_OUTPUT, full_path.c_str());
|
|
INFO_MSG(_T("\nOutput: \"%s\"\n"), full_path.c_str());
|
|
}
|
|
|
|
FILE *fp = FOPEN(build_output_filename,("w+b"));
|
|
if (!fp)
|
|
{
|
|
ERROR_MSG(_T("Can't open output file\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
if (fwrite(m_exehead,1,m_exehead_size,fp) != m_exehead_size)
|
|
{
|
|
ERROR_MSG(_T("Error: can't write %d bytes to output\n"),m_exehead_size);
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
#ifdef NSIS_CONFIG_CRC_ANAL
|
|
crc=CRC32(crc,m_exehead,m_exehead_size);
|
|
#else
|
|
crc=CRC32(crc,m_exehead+512,m_exehead_size-512);
|
|
#endif
|
|
#endif
|
|
|
|
firstheader fh={0,};
|
|
fh.nsinst[0]=FH_INT1;
|
|
fh.nsinst[1]=FH_INT2;
|
|
fh.nsinst[2]=FH_INT3;
|
|
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
fh.flags=(build_crcchk?(build_crcchk==2?FH_FLAGS_FORCE_CRC:0):FH_FLAGS_NO_CRC);
|
|
#else
|
|
fh.flags=0;
|
|
#endif
|
|
#ifdef NSIS_CONFIG_SILENT_SUPPORT
|
|
if (build_header.flags&(CH_FLAGS_SILENT|CH_FLAGS_SILENT_LOG)) fh.flags |= FH_FLAGS_SILENT;
|
|
#endif
|
|
fh.siginfo=FH_SIG;
|
|
|
|
int installinfo_compressed;
|
|
int fd_start = 0;
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (build_compress_whole)
|
|
{
|
|
int n = compressor->Init(build_compress_level, build_compress_dict_size);
|
|
if (n != C_OK)
|
|
{
|
|
ERROR_MSG(_T("Internal compiler error #12345: deflateInit() failed(%s [%d]).\n"), compressor->GetErrStr(n), n);
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
GrowBuf ihd;
|
|
{
|
|
GrowBuf hdrcomp;
|
|
|
|
PrepareHeaders(&hdrcomp);
|
|
|
|
if (add_data((char*)hdrcomp.get(),hdrcomp.getlen(),&ihd) < 0)
|
|
return PS_ERROR;
|
|
|
|
fh.length_of_header=hdrcomp.getlen();
|
|
installinfo_compressed=ihd.getlen();
|
|
}
|
|
|
|
if (!build_compress_whole)
|
|
fh.length_of_all_following_data=ihd.getlen()+build_datablock.getlen()+(int)sizeof(firstheader)+(build_crcchk?sizeof(crc32_t):0);
|
|
else
|
|
fd_start=ftell(fp);
|
|
|
|
try
|
|
{
|
|
file_writer_sink sink(fp);
|
|
firstheader_writer w(&sink);
|
|
w.write(&fh);
|
|
}
|
|
catch (...)
|
|
{
|
|
ERROR_MSG(_T("Error: can't write %d bytes to output\n"),sizeof(fh));
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (build_compress_whole) {
|
|
if (deflateToFile(fp,(char*)ihd.get(),ihd.getlen()))
|
|
{
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (fwrite(ihd.get(),1,ihd.getlen(),fp) != (unsigned int)ihd.getlen())
|
|
{
|
|
ERROR_MSG(_T("Error: can't write %d bytes to output\n"),ihd.getlen());
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
crc_writer_sink crc_sink((crc32_t *) &crc);
|
|
firstheader_writer w(&crc_sink);
|
|
w.write(&fh);
|
|
|
|
crc=CRC32(crc,(unsigned char*)ihd.get(),ihd.getlen());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
INFO_MSG(_T("Install: "));
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
int np=build_header.blocks[NB_PAGES].num;
|
|
INFO_MSG(_T("%d page%s (%d bytes), "),np,np==1?_T(""):_T("s"),np*sizeof(page));
|
|
#endif
|
|
{
|
|
int ns=build_sections.getlen()/sizeof(section);
|
|
section *s=(section*)build_sections.get();
|
|
int x;
|
|
int req=0;
|
|
for (x = 1; x < ns; x ++)
|
|
{
|
|
if (!s[x].name_ptr || s[x].flags & SF_RO) req++;
|
|
}
|
|
INFO_MSG(_T("%d section%s"),ns,ns==1?_T(""):_T("s"));
|
|
if (req)
|
|
{
|
|
INFO_MSG(_T(" (%d required)"),req);
|
|
}
|
|
INFO_MSG(_T(" (%d bytes), "), build_sections.getlen());
|
|
}
|
|
int ne=build_header.blocks[NB_ENTRIES].num;
|
|
INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ne*sizeof(entry));
|
|
int ns=build_strlist.getnum();
|
|
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),build_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR)));
|
|
int nlt=build_header.blocks[NB_LANGTABLES].num;
|
|
INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),build_langtables.getlen());
|
|
if (ubuild_entries.getlen())
|
|
{
|
|
INFO_MSG(_T("Uninstall: "));
|
|
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
|
|
np=build_uninst.blocks[NB_PAGES].num;
|
|
INFO_MSG(_T("%d page%s (%d bytes), \n"),np,np==1?_T(""):_T("s"),ubuild_pages.getlen());
|
|
#endif
|
|
{
|
|
int ns=ubuild_sections.getlen()/sizeof(section);
|
|
section *s=(section*)ubuild_sections.get();
|
|
int x;
|
|
int req=0;
|
|
for (x = 1; x < ns; x ++)
|
|
{
|
|
if (!s[x].name_ptr || s[x].flags & SF_RO) req++;
|
|
}
|
|
INFO_MSG(_T("%d section%s"),ns,ns==1?_T(""):_T("s"));
|
|
if (req)
|
|
{
|
|
INFO_MSG(_T(" (%d required)"),req);
|
|
}
|
|
INFO_MSG(_T(" (%d bytes), "), ubuild_sections.getlen());
|
|
}
|
|
ne=build_uninst.blocks[NB_ENTRIES].num;
|
|
INFO_MSG(_T("%d instruction%s (%d bytes), "),ne,ne==1?_T(""):_T("s"),ubuild_entries.getlen());
|
|
ns=ubuild_strlist.getnum();
|
|
INFO_MSG(_T("%d string%s (%d bytes), "),ns,ns==1?_T(""):_T("s"),ubuild_strlist.getcount()*(build_unicode ? sizeof(CHAR) : sizeof(TCHAR)));
|
|
nlt=build_uninst.blocks[NB_LANGTABLES].num;
|
|
INFO_MSG(_T("%d language table%s (%d bytes).\n"),nlt,nlt==1?_T(""):_T("s"),ubuild_langtables.getlen());
|
|
}
|
|
|
|
|
|
if (db_opt_save)
|
|
{
|
|
int total_out_size_estimate=
|
|
m_exehead_size+sizeof(fh)+build_datablock.getlen()+(build_crcchk?sizeof(crc32_t):0);
|
|
int pc=(int)(((INT64)db_opt_save*1000)/(db_opt_save+total_out_size_estimate));
|
|
INFO_MSG(_T("Datablock optimizer saved %d bytes (~%d.%d%%).\n"),db_opt_save,
|
|
pc/10,pc%10);
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
INFO_MSG(_T("\nUsing %s%s compression.\n\n"), compressor->GetName(), build_compress_whole?_T(" (compress whole)"):_T(""));
|
|
#endif
|
|
|
|
unsigned int total_usize=m_exehead_original_size;
|
|
|
|
INFO_MSG(_T("EXE header size: %10d / %d bytes\n"),m_exehead_size,m_exehead_original_size);
|
|
|
|
if (build_compress_whole) {
|
|
INFO_MSG(_T("Install code: (%d bytes)\n"),
|
|
sizeof(fh)+fh.length_of_header);
|
|
}
|
|
else {
|
|
INFO_MSG(_T("Install code: %10d / %d bytes\n"),
|
|
sizeof(fh)+installinfo_compressed,
|
|
sizeof(fh)+fh.length_of_header);
|
|
}
|
|
|
|
total_usize+=sizeof(fh)+fh.length_of_header;
|
|
|
|
{
|
|
int dbsize, dbsizeu;
|
|
dbsize = build_datablock.getlen();
|
|
if (uninstall_size>0) dbsize-=uninstall_size;
|
|
|
|
if (build_compress_whole) {
|
|
dbsizeu=dbsize;
|
|
INFO_MSG(_T("Install data: (%d bytes)\n"),dbsizeu);
|
|
}
|
|
else {
|
|
dbsizeu = db_full_size - uninstall_size_full;
|
|
INFO_MSG(_T("Install data: %10d / %d bytes\n"),dbsize,dbsizeu);
|
|
}
|
|
total_usize+=dbsizeu;
|
|
}
|
|
|
|
if (uninstall_size>=0)
|
|
{
|
|
if (build_compress_whole)
|
|
INFO_MSG(_T("Uninstall code+data: (%d bytes)\n"),uninstall_size_full);
|
|
else
|
|
INFO_MSG(_T("Uninstall code+data: %6d / %d bytes\n"),uninstall_size,uninstall_size_full);
|
|
total_usize+=uninstall_size_full;
|
|
}
|
|
|
|
if (build_compress_whole) {
|
|
INFO_MSG(_T("Compressed data: "));
|
|
}
|
|
|
|
if (build_datablock.getlen())
|
|
{
|
|
build_datablock.setro(TRUE);
|
|
int dbl = build_datablock.getlen();
|
|
int left = dbl;
|
|
while (left > 0)
|
|
{
|
|
int l = min(build_filebuflen, left);
|
|
char *dbptr = (char *) build_datablock.get(dbl - left, l);
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (build_compress_whole)
|
|
{
|
|
if (deflateToFile(fp,dbptr,l))
|
|
{
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
crc=CRC32(crc,(unsigned char *)dbptr,l);
|
|
#endif
|
|
if ((int)fwrite(dbptr,1,l,fp) != l)
|
|
{
|
|
ERROR_MSG(_T("Error: can't write %d bytes to output\n"),l);
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
fflush(fp);
|
|
}
|
|
build_datablock.release();
|
|
left -= l;
|
|
}
|
|
build_datablock.setro(FALSE);
|
|
build_datablock.clear();
|
|
}
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (build_compress_whole)
|
|
{
|
|
if (deflateToFile(fp,NULL,0))
|
|
{
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
compressor->End();
|
|
|
|
unsigned fend = ftell(fp);
|
|
|
|
fh.length_of_all_following_data=ftell(fp)-fd_start+(build_crcchk?sizeof(crc32_t):0);
|
|
INFO_MSG(
|
|
_T("%10d / %d bytes\n"),
|
|
ftell(fp) - fd_start,
|
|
data_block_size_before_uninst + fh.length_of_header + sizeof(firstheader) + uninstall_size_full
|
|
);
|
|
|
|
fseek(fp,fd_start,SEEK_SET);
|
|
|
|
try
|
|
{
|
|
file_writer_sink sink(fp);
|
|
firstheader_writer w(&sink);
|
|
w.write(&fh);
|
|
}
|
|
catch (...)
|
|
{
|
|
ERROR_MSG(_T("Error: can't write %d bytes to output\n"),sizeof(fh));
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
if (build_crcchk)
|
|
{
|
|
// check rest of CRC
|
|
fseek(fp,fd_start,SEEK_SET);
|
|
for (;;)
|
|
{
|
|
char buf[32768];
|
|
int l=fread(buf,1,sizeof(buf),fp);
|
|
if (!l) break;
|
|
crc=CRC32(crc,(unsigned char *)buf,l);
|
|
}
|
|
}
|
|
#endif
|
|
fseek(fp,fend,SEEK_SET); // reset eof flag
|
|
}
|
|
#endif
|
|
|
|
if (build_crcchk)
|
|
{
|
|
total_usize+=sizeof(int);
|
|
int rcrc = FIX_ENDIAN_INT32(crc);
|
|
if (fwrite(&rcrc,1,sizeof(crc32_t),fp) != sizeof(crc32_t))
|
|
{
|
|
ERROR_MSG(_T("Error: can't write %d bytes to output\n"),sizeof(crc32_t));
|
|
fclose(fp);
|
|
return PS_ERROR;
|
|
}
|
|
INFO_MSG(_T("CRC (0x%08X): 4 / 4 bytes\n"),crc);
|
|
}
|
|
INFO_MSG(_T("\n"));
|
|
{
|
|
UINT pc=(UINT)(((UINT64)ftell(fp)*1000)/(total_usize));
|
|
INFO_MSG(_T("Total size: %10u / %u bytes (%u.%u%%)\n"),
|
|
ftell(fp),total_usize,pc/10,pc%10);
|
|
}
|
|
fclose(fp);
|
|
if (postbuild_cmds)
|
|
{
|
|
for (struct postbuild_cmd *cmd=postbuild_cmds; cmd; cmd = cmd->next)
|
|
{
|
|
LPTSTR cmdstr = cmd->cmd, cmdstrbuf = NULL;
|
|
LPTSTR arg = _tcsstr(cmdstr, _T("%1"));
|
|
if (arg) // if found, replace %1 by build_output_filename
|
|
{
|
|
const UINT cchbldoutfile = _tcslen(build_output_filename);
|
|
cmdstrbuf = (LPTSTR) malloc( (_tcslen(cmdstr) + cchbldoutfile + 1)*sizeof(TCHAR) );
|
|
if (!cmdstrbuf)
|
|
{
|
|
ERROR_MSG(_T("Error: can't allocate memory for finalize command\n"));
|
|
return PS_ERROR;
|
|
}
|
|
arg -= (UINT_PTR)cmdstr, arg += (UINT_PTR)cmdstrbuf;
|
|
_tcscpy(cmdstrbuf,cmdstr);
|
|
cmdstr = cmdstrbuf;
|
|
memmove(arg+cchbldoutfile, arg+2, (_tcslen(arg+2)+1)*sizeof(TCHAR));
|
|
memmove(arg, build_output_filename, cchbldoutfile*sizeof(TCHAR));
|
|
}
|
|
|
|
SCRIPT_MSG(_T("\nFinalize command: %s\n"),cmdstr);
|
|
#ifdef _WIN32
|
|
int ret=sane_system(cmdstr);
|
|
#else
|
|
PATH_CONVERT(cmdstr);
|
|
int ret=system(cmdstr);
|
|
#endif
|
|
if (ret != 0)
|
|
INFO_MSG(_T("Finalize command returned %d\n"),ret);
|
|
free(cmdstrbuf);
|
|
}
|
|
}
|
|
print_warnings();
|
|
return PS_OK;
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
int CEXEBuild::deflateToFile(FILE *fp, char *buf, int len) // len==0 to flush
|
|
{
|
|
build_compressor_set=true;
|
|
|
|
char obuf[65536];
|
|
bool flush=false;
|
|
compressor->SetNextIn(buf,len);
|
|
if (!buf||!len)
|
|
{
|
|
char a;
|
|
compressor->SetNextIn(&a,0);
|
|
flush=C_FINISH;
|
|
}
|
|
for (;;)
|
|
{
|
|
compressor->SetNextOut(obuf,sizeof(obuf));
|
|
int ret=compressor->Compress(flush);
|
|
if (ret<0 && (ret!=-1 || !flush))
|
|
{
|
|
ERROR_MSG(_T("Error: deflateToFile: deflate() failed(%s [%d])\n"), compressor->GetErrStr(ret), ret);
|
|
return 1;
|
|
}
|
|
int l=compressor->GetNextOut()-obuf;
|
|
if (l)
|
|
{
|
|
if (fwrite(obuf,1,l,fp) != (unsigned)l)
|
|
{
|
|
ERROR_MSG(_T("Error: deflateToFile fwrite(%d) failed\n"),l);
|
|
return 1;
|
|
}
|
|
fflush(fp);
|
|
}
|
|
if (!compressor->GetAvailIn() && (!flush || !l)) break;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int CEXEBuild::uninstall_generate()
|
|
{
|
|
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
|
|
if (ubuild_entries.getlen() && uninstaller_writes_used)
|
|
{
|
|
SCRIPT_MSG(_T("Generating uninstaller... "));
|
|
|
|
firstheader fh={0,};
|
|
|
|
GrowBuf uhd;
|
|
{
|
|
GrowBuf udata;
|
|
|
|
set_uninstall_mode(1);
|
|
|
|
PrepareHeaders(&udata);
|
|
|
|
fh.length_of_header=udata.getlen();
|
|
int err=add_data((char*)udata.get(),udata.getlen(),&uhd);
|
|
set_uninstall_mode(0);
|
|
if (err < 0) return PS_ERROR;
|
|
}
|
|
|
|
crc32_t crc=0;
|
|
|
|
// Get offsets of icons to replace for uninstall
|
|
// Also makes sure that the icons are there and in the right size.
|
|
LPBYTE unicon_data = generate_uninstall_icon_data(installer_icon, uninstaller_icon, m_unicon_size);
|
|
if (generate_unicons_offsets(m_exehead, m_exehead_size, unicon_data, IDI_ICON2) == 0)
|
|
{
|
|
delete [] unicon_data;
|
|
return PS_ERROR;
|
|
}
|
|
|
|
entry *ent = (entry *) build_entries.get();
|
|
if (!ent)
|
|
{
|
|
delete [] unicon_data;
|
|
return PS_ERROR;
|
|
}
|
|
|
|
int ents = build_header.blocks[NB_ENTRIES].num;
|
|
int uns = uninstaller_writes_used;
|
|
int uninstdata_offset = build_datablock.getlen();
|
|
while (ents--)
|
|
{
|
|
if (ent->which == EW_WRITEUNINSTALLER)
|
|
{
|
|
ent->offsets[1] = uninstdata_offset;
|
|
ent->offsets[2] = m_unicon_size;
|
|
uns--;
|
|
if (!uns)
|
|
break;
|
|
}
|
|
ent++;
|
|
}
|
|
|
|
if (add_db_data((char *)unicon_data,m_unicon_size) < 0)
|
|
{
|
|
delete [] unicon_data;
|
|
return PS_ERROR;
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
{
|
|
// "create" the uninstaller
|
|
LPBYTE uninst_header = (LPBYTE) malloc(m_exehead_size);
|
|
if (!uninst_header)
|
|
return PS_ERROR;
|
|
|
|
memcpy(uninst_header, m_exehead, m_exehead_size);
|
|
|
|
// patch the icons
|
|
LPBYTE seeker = unicon_data;
|
|
while (*seeker) {
|
|
DWORD dwSize = FIX_ENDIAN_INT32(*(LPDWORD) seeker);
|
|
seeker += sizeof(DWORD);
|
|
DWORD dwOffset = FIX_ENDIAN_INT32(*(LPDWORD) seeker);
|
|
seeker += sizeof(DWORD);
|
|
memcpy(uninst_header + dwOffset, seeker, dwSize);
|
|
seeker += dwSize;
|
|
}
|
|
|
|
delete [] unicon_data;
|
|
|
|
#ifdef NSIS_CONFIG_CRC_ANAL
|
|
crc=CRC32(crc, uninst_header, m_exehead_size);
|
|
#else
|
|
crc=CRC32(crc, uninst_header + 512, m_exehead_size - 512);
|
|
#endif
|
|
|
|
free(uninst_header);
|
|
}
|
|
#endif
|
|
fh.nsinst[0]=FH_INT1;
|
|
fh.nsinst[1]=FH_INT2;
|
|
fh.nsinst[2]=FH_INT3;
|
|
fh.flags=FH_FLAGS_UNINSTALL;
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
fh.flags|=(build_crcchk?(build_crcchk==2?FH_FLAGS_FORCE_CRC:0):FH_FLAGS_NO_CRC);
|
|
#endif
|
|
#ifdef NSIS_CONFIG_SILENT_SUPPORT
|
|
if (build_uninst.flags&(CH_FLAGS_SILENT|CH_FLAGS_SILENT_LOG)) fh.flags |= FH_FLAGS_SILENT;
|
|
#endif
|
|
fh.siginfo=FH_SIG;
|
|
fh.length_of_all_following_data=
|
|
uhd.getlen()+ubuild_datablock.getlen()+(int)sizeof(firstheader)+(build_crcchk?sizeof(crc32_t):0);
|
|
|
|
MMapBuf udata;
|
|
|
|
{
|
|
growbuf_writer_sink sink(&udata, build_unicode);
|
|
firstheader_writer w(&sink);
|
|
w.write(&fh);
|
|
}
|
|
|
|
ubuild_datablock.setro(TRUE);
|
|
|
|
#ifdef NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
if (build_compress_whole) {
|
|
// compress uninstaller too
|
|
{
|
|
char obuf[65536];
|
|
int n = compressor->Init(build_compress_level, build_compress_dict_size);
|
|
if (n != C_OK)
|
|
{
|
|
ERROR_MSG(_T("Internal compiler error #12345: deflateInit() failed(%s [%d]).\n"), compressor->GetErrStr(n), n);
|
|
extern void quit(); quit();
|
|
}
|
|
|
|
compressor->SetNextIn((char*)uhd.get(), uhd.getlen());
|
|
while (compressor->GetAvailIn())
|
|
{
|
|
compressor->SetNextOut(obuf, sizeof(obuf));
|
|
compressor->Compress(0);
|
|
if (compressor->GetNextOut() - obuf > 0)
|
|
{
|
|
udata.add(obuf, compressor->GetNextOut() - obuf);
|
|
}
|
|
}
|
|
|
|
int avail_in = ubuild_datablock.getlen();
|
|
int in_pos = 0;
|
|
while (avail_in > 0) {
|
|
int l = min(avail_in, build_filebuflen);
|
|
|
|
char *p = (char*)ubuild_datablock.get(in_pos, l);
|
|
compressor->SetNextIn(p, l);
|
|
|
|
while (compressor->GetAvailIn())
|
|
{
|
|
compressor->SetNextOut(obuf, sizeof(obuf));
|
|
compressor->Compress(0);
|
|
if (compressor->GetNextOut() - obuf > 0)
|
|
udata.add(obuf, compressor->GetNextOut() - obuf);
|
|
}
|
|
|
|
ubuild_datablock.release();
|
|
|
|
avail_in -= l;
|
|
in_pos += l;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
compressor->SetNextOut(obuf, sizeof(obuf));
|
|
compressor->Compress(C_FINISH);
|
|
if (compressor->GetNextOut() - obuf > 0)
|
|
udata.add(obuf, compressor->GetNextOut() - obuf);
|
|
else break;
|
|
}
|
|
compressor->End();
|
|
}
|
|
|
|
firstheader *_fh=(firstheader *)udata.get(0, sizeof(firstheader));
|
|
_fh->length_of_all_following_data=FIX_ENDIAN_INT32(udata.getlen()+(build_crcchk?sizeof(crc32_t):0));
|
|
udata.release();
|
|
}
|
|
else
|
|
#endif//NSIS_CONFIG_COMPRESSION_SUPPORT
|
|
{
|
|
udata.add(uhd.get(), uhd.getlen());
|
|
|
|
int st = udata.getlen();
|
|
int length = ubuild_datablock.getlen();
|
|
int left = length;
|
|
udata.resize(st + left);
|
|
while (left > 0)
|
|
{
|
|
int l = min(build_filebuflen, left);
|
|
void *p = ubuild_datablock.get(length - left, l);
|
|
memcpy(udata.get(st + length - left, l), p, l);
|
|
udata.flush(l);
|
|
udata.release();
|
|
ubuild_datablock.release();
|
|
left -= l;
|
|
}
|
|
}
|
|
|
|
ubuild_datablock.clear();
|
|
|
|
udata.setro(TRUE);
|
|
|
|
#ifdef NSIS_CONFIG_CRC_SUPPORT
|
|
if (build_crcchk)
|
|
{
|
|
int pos = 0;
|
|
int left = udata.getlen();
|
|
while (left > 0)
|
|
{
|
|
int l = min(build_filebuflen, left);
|
|
crc = CRC32(crc, (unsigned char *) udata.get(pos, l), l);
|
|
udata.release();
|
|
pos += l;
|
|
left -= l;
|
|
}
|
|
udata.setro(FALSE);
|
|
FIX_ENDIAN_INT32_INPLACE(crc);
|
|
udata.add(&crc, sizeof(crc));
|
|
udata.setro(TRUE);
|
|
}
|
|
#endif
|
|
|
|
if (add_db_data(&udata) < 0)
|
|
return PS_ERROR;
|
|
|
|
udata.clear();
|
|
|
|
//uninstall_size_full=fh.length_of_all_following_data + sizeof(int) + unicondata_size - 32 + sizeof(int);
|
|
uninstall_size_full=fh.length_of_all_following_data+m_unicon_size;
|
|
|
|
// compressed size
|
|
uninstall_size=build_datablock.getlen()-uninstdata_offset;
|
|
|
|
SCRIPT_MSG(_T("Done!\n"));
|
|
}
|
|
#endif
|
|
return PS_OK;
|
|
}
|
|
|
|
#define SWAP(x,y,i) { i _ii; _ii=x; x=y; y=_ii; }
|
|
|
|
void CEXEBuild::set_uninstall_mode(int un)
|
|
{
|
|
if (un != uninstall_mode)
|
|
{
|
|
uninstall_mode=un;
|
|
if (un)
|
|
{
|
|
cur_datablock=&ubuild_datablock;
|
|
cur_datablock_cache=&ubuild_datablock_cache;
|
|
cur_entries=&ubuild_entries;
|
|
cur_instruction_entry_map=&ubuild_instruction_entry_map;
|
|
cur_functions=&ubuild_functions;
|
|
cur_labels=&ubuild_labels;
|
|
cur_pages=&ubuild_pages;
|
|
cur_sections=&ubuild_sections;
|
|
cur_header=&build_uninst;
|
|
cur_strlist=&ubuild_strlist;
|
|
cur_langtables=&ubuild_langtables;
|
|
cur_ctlcolors=&ubuild_ctlcolors;
|
|
|
|
definedlist.add(_T("__UNINSTALL__"));
|
|
}
|
|
else
|
|
{
|
|
cur_datablock=&build_datablock;
|
|
cur_datablock_cache=&build_datablock_cache;
|
|
cur_entries=&build_entries;
|
|
cur_instruction_entry_map=&build_instruction_entry_map;
|
|
cur_functions=&build_functions;
|
|
cur_labels=&build_labels;
|
|
cur_pages=&build_pages;
|
|
cur_sections=&build_sections;
|
|
cur_header=&build_header;
|
|
cur_strlist=&build_strlist;
|
|
cur_langtables=&build_langtables;
|
|
cur_ctlcolors=&build_ctlcolors;
|
|
|
|
definedlist.del(_T("__UNINSTALL__"));
|
|
}
|
|
|
|
SWAP(db_opt_save_u,db_opt_save,int);
|
|
SWAP(db_comp_save_u,db_comp_save,int);
|
|
SWAP(db_full_size_u,db_full_size,int);
|
|
}
|
|
}
|
|
|
|
extern FILE *g_output;
|
|
|
|
/* Useful for debugging.
|
|
bool IsStringASCII(const TCHAR* s)
|
|
{
|
|
while (*s) { if (!_istascii(*s++)) return false; }
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
void CEXEBuild::warning(const TCHAR *s, ...)
|
|
{
|
|
TCHAR buf[NSIS_MAX_STRLEN*10];
|
|
va_list val;
|
|
va_start(val,s);
|
|
#ifdef _WIN32
|
|
_vstprintf(buf,s,val);
|
|
#else
|
|
_vsntprintf(buf,NSIS_MAX_STRLEN*10,s,val);
|
|
#endif
|
|
va_end(val);
|
|
|
|
m_warnings.add(buf,0);
|
|
notify(MAKENSIS_NOTIFY_WARNING,buf);
|
|
if (display_warnings)
|
|
{
|
|
PrintColorFmtMsg_WARN(_T("warning: %s\n"),buf);
|
|
}
|
|
}
|
|
|
|
void CEXEBuild::warning_fl(const TCHAR *s, ...)
|
|
{
|
|
TCHAR buf[NSIS_MAX_STRLEN*10];
|
|
va_list val;
|
|
va_start(val,s);
|
|
#ifdef _WIN32
|
|
_vstprintf(buf,s,val);
|
|
#else
|
|
_vsntprintf(buf,NSIS_MAX_STRLEN*10,s,val);
|
|
#endif
|
|
va_end(val);
|
|
_stprintf(buf+_tcslen(buf),_T(" (%s:%d)"),curfilename,linecnt);
|
|
|
|
m_warnings.add(buf,0);
|
|
notify(MAKENSIS_NOTIFY_WARNING,buf);
|
|
if (display_warnings)
|
|
{
|
|
PrintColorFmtMsg_WARN(_T("warning: %s\n"),buf);
|
|
}
|
|
}
|
|
|
|
void CEXEBuild::ERROR_MSG(const TCHAR *s, ...) const
|
|
{
|
|
if (display_errors || notify_hwnd)
|
|
{
|
|
TCHAR buf[NSIS_MAX_STRLEN*10];
|
|
va_list val;
|
|
va_start(val,s);
|
|
#ifdef _WIN32
|
|
_vstprintf(buf,s,val);
|
|
#else
|
|
_vsntprintf(buf,NSIS_MAX_STRLEN*10,s,val);
|
|
#endif
|
|
va_end(val);
|
|
notify(MAKENSIS_NOTIFY_ERROR,buf);
|
|
if (display_errors)
|
|
{
|
|
PrintColorFmtMsg_ERR(_T("%s"),buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEXEBuild::SCRIPT_MSG(const TCHAR *s, ...) const
|
|
{
|
|
if (display_script)
|
|
{
|
|
va_list val;
|
|
va_start(val,s);
|
|
_vftprintf(g_output,s,val);
|
|
va_end(val);
|
|
fflush(g_output);
|
|
}
|
|
}
|
|
|
|
void CEXEBuild::INFO_MSG(const TCHAR *s, ...) const
|
|
{
|
|
if (display_info)
|
|
{
|
|
va_list val;
|
|
va_start(val,s);
|
|
_vftprintf(g_output,s,val);
|
|
va_end(val);
|
|
fflush(g_output);
|
|
}
|
|
}
|
|
|
|
void CEXEBuild::print_warnings()
|
|
{
|
|
int nw=0,x=m_warnings.getcount();
|
|
if (!x || !display_warnings) return;
|
|
TCHAR *p=m_warnings.get();
|
|
while (x>0) if (!p[--x]) nw++;
|
|
SetPrintColorWARN();
|
|
_ftprintf(g_output,_T("\n%d warning%s:\n"),nw,nw==1?_T(""):_T("s"));
|
|
for (x = 0; x < nw; x ++)
|
|
{
|
|
_ftprintf(g_output,_T(" %s\n"),p);
|
|
p+=_tcslen(p)+1;
|
|
}
|
|
FlushOutputAndResetPrintColor();
|
|
}
|
|
|
|
void CEXEBuild::notify(notify_e code, const TCHAR *data) const
|
|
{
|
|
#ifdef _WIN32
|
|
if (notify_hwnd)
|
|
{
|
|
COPYDATASTRUCT cds = {(DWORD)code, (_tcslen(data)+1)*sizeof(TCHAR), (void *) data};
|
|
SendMessage(notify_hwnd, WM_COPYDATA, 0, (LPARAM)&cds);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef NSIS_CONFIG_PLUGIN_SUPPORT
|
|
int CEXEBuild::initialize_default_plugins(bool newtargetarc)
|
|
{
|
|
if (!m_pPlugins)
|
|
{
|
|
plugin_used = uninst_plugin_used = false;
|
|
newtargetarc = true;
|
|
}
|
|
if (!newtargetarc) return PS_OK;
|
|
|
|
m_pPlugins = &m_plugins[m_target_type];
|
|
|
|
tstring searchPath = definedlist.find(_T("NSISDIR"));
|
|
searchPath += PLATFORM_PATH_SEPARATOR_STR _T("Plugins") PLATFORM_PATH_SEPARATOR_STR;
|
|
searchPath += get_target_suffix();
|
|
|
|
SCRIPT_MSG(_T("Processing default plugins: \"%s") PLATFORM_PATH_SEPARATOR_STR _T("*.dll\"\n"), searchPath.c_str());
|
|
if (!m_pPlugins->Initialize(searchPath.c_str(), !!display_script))
|
|
{
|
|
ERROR_MSG(_T("Error initializing default plugins!\n"));
|
|
return PS_ERROR;
|
|
}
|
|
SCRIPT_MSG(_T("\n"));
|
|
return PS_OK;
|
|
}
|
|
|
|
int CEXEBuild::add_plugins_dir_initializer(void)
|
|
{
|
|
if (!plugin_used && !uninst_plugin_used) return PS_OK;
|
|
|
|
SCRIPT_MSG(_T("Adding plug-ins initializing function... "));
|
|
|
|
bool uninstall = !plugin_used;
|
|
|
|
int ret;
|
|
int zero_offset;
|
|
|
|
int var_zero;
|
|
var_zero=m_UserVarNames.get(_T("0"));
|
|
|
|
again:
|
|
// Function [un.]Initialize_____Plugins
|
|
ret=add_function(uninstall?_T("un.Initialize_____Plugins"):_T("Initialize_____Plugins"));
|
|
if (ret != PS_OK) return ret;
|
|
|
|
// don't move this, depends on [un.]
|
|
zero_offset=add_string(_T("$0"));
|
|
|
|
// SetDetailsPrint none
|
|
ret=add_entry_direct(EW_SETFLAG, FLAG_OFFSET(status_update), add_intstring(6));
|
|
if (ret != PS_OK) return ret;
|
|
|
|
// StrCmp $PLUGINSDIR ""
|
|
ret=add_entry_direct(EW_STRCMP, add_string(_T("$PLUGINSDIR")), 0, 0, ns_label.add(_T("Initialize_____Plugins_done"),0));
|
|
if (ret != PS_OK) return ret;
|
|
// Push $0
|
|
ret=add_entry_direct(EW_PUSHPOP, zero_offset);
|
|
if (ret != PS_OK) return ret;
|
|
// ClearErrors
|
|
ret=add_entry_direct(EW_SETFLAG, FLAG_OFFSET(exec_error));
|
|
if (ret != PS_OK) return ret;
|
|
// GetTempFileName $0
|
|
ret=add_entry_direct(EW_GETTEMPFILENAME, var_zero, add_string(_T("$TEMP")));
|
|
if (ret != PS_OK) return ret;
|
|
// Delete $0 [simple, nothing that could clash with special temp permissions]
|
|
ret=add_entry_direct(EW_DELETEFILE, zero_offset, DEL_SIMPLE);
|
|
if (ret != PS_OK) return ret;
|
|
// CraeteDirectory $0 - a dir instead of that temp file
|
|
ret=add_entry_direct(EW_CREATEDIR, zero_offset);
|
|
if (ret != PS_OK) return ret;
|
|
// IfErrors Initialize_____Plugins_error - detect errors
|
|
ret=add_entry_direct(EW_IFFLAG, ns_label.add(_T("Initialize_____Plugins_error"),0), 0, FLAG_OFFSET(exec_error));
|
|
if (ret != PS_OK) return ret;
|
|
// Copy $0 to $PLUGINSDIR
|
|
ret=add_entry_direct(EW_ASSIGNVAR, m_UserVarNames.get(_T("PLUGINSDIR")), zero_offset);
|
|
if (ret != PS_OK) return ret;
|
|
// Pop $0
|
|
ret=add_entry_direct(EW_PUSHPOP, var_zero, 1);
|
|
if (ret != PS_OK) return ret;
|
|
|
|
// done
|
|
if (add_label(_T("Initialize_____Plugins_done"))) return PS_ERROR;
|
|
// Return
|
|
ret=add_entry_direct(EW_RET);
|
|
if (ret != PS_OK) return ret;
|
|
|
|
// error
|
|
if (add_label(_T("Initialize_____Plugins_error"))) return PS_ERROR;
|
|
// 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.")));
|
|
if (ret != PS_OK) return ret;
|
|
// Quit
|
|
ret=add_entry_direct(EW_QUIT);
|
|
if (ret != PS_OK) return ret;
|
|
|
|
// FunctionEnd
|
|
ret=function_end();
|
|
if (ret != PS_OK) return ret;
|
|
|
|
if (uninst_plugin_used && !uninstall) {
|
|
uninstall = true;
|
|
goto again;
|
|
}
|
|
|
|
SCRIPT_MSG(_T("Done!\n"));
|
|
|
|
return PS_OK;
|
|
}
|
|
#endif // NSIS_CONFIG_PLUGIN_SUPPORT
|
|
|
|
void CEXEBuild::init_res_editor()
|
|
{
|
|
build_compressor_set = true;
|
|
if (!res_editor)
|
|
res_editor = new CResourceEditor(m_exehead, m_exehead_size);
|
|
}
|
|
|
|
void CEXEBuild::close_res_editor()
|
|
{
|
|
if (!res_editor) return;
|
|
DWORD newsize;
|
|
|
|
// get size
|
|
newsize = res_editor->Save(NULL, newsize);
|
|
unsigned char *new_header = new unsigned char[newsize];
|
|
|
|
// save
|
|
int rc = res_editor->Save(new_header, newsize);
|
|
assert(rc == 0);
|
|
|
|
update_exehead(new_header, newsize);
|
|
|
|
// TODO: resource-controlling class
|
|
delete [] new_header;
|
|
|
|
delete res_editor;
|
|
res_editor=0;
|
|
}
|
|
|
|
int CEXEBuild::DeclaredUserVar(const TCHAR *szVarName)
|
|
{
|
|
if (m_ShellConstants.get((TCHAR*)szVarName) >= 0)
|
|
{
|
|
ERROR_MSG(_T("Error: name \"%s\" in use by constant\n"), szVarName);
|
|
return PS_ERROR;
|
|
}
|
|
|
|
int idxUserVar = m_UserVarNames.get((TCHAR*)szVarName);
|
|
if (idxUserVar >= 0)
|
|
{
|
|
ERROR_MSG(_T("Error: variable \"%s\" already declared\n"), szVarName);
|
|
return PS_ERROR;
|
|
}
|
|
const TCHAR *pVarName = szVarName;
|
|
int iVarLen = _tcslen(szVarName);
|
|
|
|
if (iVarLen > 60)
|
|
{
|
|
ERROR_MSG(_T("Error: variable name too long!\n"));
|
|
return PS_ERROR;
|
|
}
|
|
else if (!iVarLen)
|
|
{
|
|
ERROR_MSG(_T("Error: variable with empty name!\n"));
|
|
return PS_ERROR;
|
|
}
|
|
else
|
|
{
|
|
while (*pVarName)
|
|
{
|
|
if (!isSimpleChar(*pVarName))
|
|
{
|
|
ERROR_MSG(_T("Error: invalid characters in variable name \"%s\", use only characters [a-z][A-Z][0-9] and '_'\n"), szVarName);
|
|
return PS_ERROR;
|
|
}
|
|
pVarName++;
|
|
}
|
|
}
|
|
|
|
m_UserVarNames.add(szVarName);
|
|
if (m_UserVarNames.getnum() > MAX_CODED)
|
|
{
|
|
ERROR_MSG(_T("Error: too many user variables declared. Maximum allowed is %u.\n"), MAX_CODED - m_iBaseVarsNum);
|
|
return PS_ERROR;
|
|
}
|
|
return PS_OK;
|
|
}
|
|
|
|
|
|
int CEXEBuild::GetUserVarIndex(LineParser &line, int token)
|
|
{
|
|
TCHAR *p = line.gettoken_str(token);
|
|
if ( *p == _T('$') && *(p+1) )
|
|
{
|
|
int idxUserVar = m_UserVarNames.get((TCHAR *)p+1);
|
|
if (idxUserVar >= 0 && m_UserVarNames.get_reference(idxUserVar) >= 0)
|
|
{
|
|
m_UserVarNames.inc_reference(idxUserVar);
|
|
return idxUserVar;
|
|
}
|
|
else
|
|
{
|
|
int idxConst = m_ShellConstants.get((TCHAR *)p+1);
|
|
if (idxConst >= 0)
|
|
{
|
|
ERROR_MSG(_T("Error: cannot change constants : %s\n"), p);
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void CEXEBuild::VerifyDeclaredUserVarRefs(UserVarsStringList *pVarsStringList)
|
|
{
|
|
for (int i = m_iBaseVarsNum; i < pVarsStringList->getnum(); i++)
|
|
{
|
|
if (!pVarsStringList->get_reference(i))
|
|
{
|
|
warning(_T("Variable \"%s\" not referenced or never set, wasting memory!"), pVarsStringList->idx2name(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CEXEBuild::set_target_architecture_predefines()
|
|
{
|
|
definedlist.del(_T("NSIS_UNICODE"));
|
|
definedlist.del(_T("NSIS_CHAR_SIZE"));
|
|
if (build_unicode)
|
|
{
|
|
definedlist.add(_T("NSIS_UNICODE"));
|
|
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("2"));
|
|
}
|
|
else
|
|
{
|
|
definedlist.add(_T("NSIS_CHAR_SIZE"), _T("1"));
|
|
}
|
|
}
|
|
|
|
int CEXEBuild::change_target_architecture()
|
|
{
|
|
if (build_compressor_set)
|
|
{
|
|
ERROR_MSG(_T("Error: Can't change target architecture after data already got compressed!\n"));
|
|
return PS_ERROR;
|
|
}
|
|
|
|
m_target_type = build_unicode ? TARGET_X86UNICODE : TARGET_X86ANSI;
|
|
set_target_architecture_predefines();
|
|
|
|
int ec = load_stub();
|
|
#ifdef NSIS_CONFIG_PLUGIN_SUPPORT
|
|
if (PS_OK==ec) ec = initialize_default_plugins(true);
|
|
#endif
|
|
return ec;
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
int CEXEBuild::set_target_charset(bool unicode)
|
|
{
|
|
build_unicode = unicode;
|
|
return change_target_architecture();
|
|
}
|
|
#endif
|
|
|
|
int CEXEBuild::set_compressor(const tstring& compressor, const bool solid) {
|
|
stub_filename = stubs_dir + PLATFORM_PATH_SEPARATOR_STR + compressor;
|
|
if (solid)
|
|
stub_filename += _T("_solid");
|
|
return load_stub();
|
|
}
|
|
|
|
CEXEBuild::TARGETTYPE CEXEBuild::get_target_type(const TCHAR*s) const
|
|
{
|
|
for(int i = CEXEBuild::TARGETFIRST; i < CEXEBuild::TARGETCOUNT; ++i)
|
|
{
|
|
CEXEBuild::TARGETTYPE tt = (CEXEBuild::TARGETTYPE) i;
|
|
if (!_tcsicmp(get_target_suffix(tt),s)) return tt;
|
|
}
|
|
return TARGET_UNKNOWN;
|
|
}
|
|
|
|
const TCHAR* CEXEBuild::get_target_suffix(CEXEBuild::TARGETTYPE tt) const
|
|
{
|
|
switch(tt)
|
|
{
|
|
case TARGET_X86ANSI :return _T("x86-ansi");
|
|
case TARGET_X86UNICODE:return _T("x86-unicode");
|
|
default:return _T("?");
|
|
}
|
|
}
|
|
|
|
int CEXEBuild::load_stub()
|
|
{
|
|
return update_exehead(stub_filename+_T("-")+get_target_suffix(), &m_exehead_original_size);
|
|
}
|
|
|
|
int CEXEBuild::update_exehead(const tstring& file, size_t *size/*=NULL*/) {
|
|
FILE *tmpfile = FOPEN(file.c_str(), ("rb"));
|
|
if (!tmpfile)
|
|
{
|
|
ERROR_MSG(_T("Error: opening stub \"%s\"\n"), file.c_str());
|
|
return PS_ERROR;
|
|
}
|
|
|
|
fseek(tmpfile, 0, SEEK_END);
|
|
size_t exehead_size = ftell(tmpfile);
|
|
|
|
unsigned char *exehead = new unsigned char[exehead_size];
|
|
fseek(tmpfile, 0, SEEK_SET);
|
|
if (fread(exehead, 1, exehead_size, tmpfile) != exehead_size)
|
|
{
|
|
ERROR_MSG(_T("Error: reading stub \"%s\"\n"), file.c_str());
|
|
fclose(tmpfile);
|
|
delete [] exehead;
|
|
return PS_ERROR;
|
|
}
|
|
fclose(tmpfile);
|
|
|
|
update_exehead(exehead, exehead_size);
|
|
|
|
if (size)
|
|
*size = exehead_size;
|
|
|
|
delete [] exehead;
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
void CEXEBuild::update_exehead(const unsigned char *new_exehead, size_t new_size) {
|
|
assert(m_exehead != new_exehead);
|
|
|
|
// align exehead to 512
|
|
m_exehead_size = align_to_512(new_size);
|
|
|
|
delete [] m_exehead;
|
|
m_exehead = new unsigned char[m_exehead_size];
|
|
|
|
// copy exehead
|
|
memcpy(m_exehead, new_exehead, new_size);
|
|
|
|
// zero rest of exehead
|
|
memset(m_exehead + new_size, 0, m_exehead_size - new_size);
|
|
}
|
|
|
|
void CEXEBuild::set_code_type_predefines(const TCHAR *value)
|
|
{
|
|
definedlist.del(_T("__SECTION__"));
|
|
definedlist.del(_T("__FUNCTION__"));
|
|
definedlist.del(_T("__PAGEEX__"));
|
|
definedlist.del(_T("__GLOBAL__"));
|
|
|
|
switch (GetCurrentTokenPlace())
|
|
{
|
|
case TP_SEC:
|
|
definedlist.add(_T("__SECTION__"), value==NULL?_T(""):value);
|
|
break;
|
|
case TP_FUNC:
|
|
definedlist.add(_T("__FUNCTION__"), value==NULL?_T(""):value);
|
|
break;
|
|
case TP_PAGEEX:
|
|
definedlist.add(_T("__PAGEEX__"), value==NULL?_T(""):value);
|
|
break;
|
|
default:
|
|
definedlist.add(_T("__GLOBAL__"));
|
|
}
|
|
}
|
|
|